BasicDrawing
============

* :download:`Download example <PyObjCExample-BasicDrawing.zip>`

A python version of the BasicDrawing example in the book
"Programming with Quartz". This shows how to use most of the core API's in
Quartz/CoreGraphics.


.. rst-class:: tabber

Sources
-------

.. rst-class:: tabbertab

AppDrawing.py
.............

.. sourcecode:: python

    import BitmapContext
    import Cocoa
    import ColorAndGState
    import CoordinateSystem
    import DrawingBasics
    import EPSPrinting
    import ImageMasking
    import Images
    import PathDrawing
    import PatternDrawing
    import QuartzTextDrawing
    import Shadings
    import ShadowsAndTransparencyLayers
    import UIHandling
    import Utilities
    
    # Defines
    kCatPDF = "Kitty.pdf"
    kPDFForBlendMode = "blendmode.pdf"
    
    kOurJPEG = "Poot.jpg"
    kQTImage = "ptlobos.tif"
    kOurSubstituteJPG = "LyingOnDeckNoProfile.JPG"
    kOurEPS = "imageturkey.eps"
    
    RAW_IMAGE_WIDTH = 400
    RAW_IMAGE_HEIGHT = 300
    kRawColorImage = "image-400x300x24.raw"
    kOtherColorImage = "otherimage-400x300x24.raw"
    
    MASKING_IMAGE_WIDTH = 400
    MASKING_IMAGE_HEIGHT = 259
    kMaskingImage = "400x259x8.bw.raw"
    
    noOffScreen = 0
    bitmapOffScreen = 1
    layerOffScreen = 2
    
    # Cache info for GetURL
    _mainBundle = None
    _urlMap = {}
    
    
    def GetURL(name):
        """
        Returns the CFURLRef for an embedded resource, or None of that cannot be found.
        """
    
        global _mainBundle
        if _mainBundle is None:
            _mainBundle = Utilities.getAppBundle()
    
        mainBundle = _mainBundle
        if mainBundle is not None:
            if name in _urlMap:
                return _urlMap[name]
    
            url = Cocoa.CFBundleCopyResourceURL(mainBundle, name, None, None)
            _urlMap[name] = url
        else:
            print("Can't get the app bundle!")
            return
    
        if url is None:
            print(f"Couldn't get URL for {name!r}")
    
        return url
    
    
    #
    # Helper functions for drawing
    #
    
    
    def callPDFDrawProc(context, proc, pdfFile):
        ourPDFurl = GetURL(pdfFile)
    
        if ourPDFurl:
            proc(context, ourPDFurl)
    
    
    def doDrawJPEGFile(context):
        ourJPEGurl = GetURL(kOurJPEG)
    
        if ourJPEGurl is not None:
            Images.drawJPEGImage(context, ourJPEGurl)
    
    
    def doRawImageFileWithURL(context):
        url = GetURL(kRawColorImage)
    
        if url is not None:
            Images.drawImageFromURL(
                context, url, RAW_IMAGE_WIDTH, RAW_IMAGE_HEIGHT, 8, True
            )
            # 8 bits per component, isColor = True
    
    
    def doRawImageFileWithCallbacks(context):
        url = GetURL(kRawColorImage)
    
        if url is not None:
            Images.doImageWithCallbacksCreatedFromURL(
                context, url, RAW_IMAGE_WIDTH, RAW_IMAGE_HEIGHT, 8, True
            )
            # 8 bits per component, isColor = True
    
    
    def doDrawImageWithCGImageSource(context):
        url = GetURL(kOurJPEG)
        if url is not None:
            Images.drawImageWithCGImageDataSource(context, url)
    
    
    def doIncrementalImage(context):
        url = GetURL(kOurJPEG)
    
        if url is not None:
            Images.doIncrementalImageWithURL(context, url)
    
    
    def doQTImage(context):
        url = GetURL(kQTImage)
    
        if url is not None:
            Images.drawQTImageWithQuartz(context, url)
    
    
    def doJPEGDocumentWithMultipleProfiles(context):
        url = GetURL(kOurSubstituteJPG)
    
        if url is not None:
            Images.drawJPEGDocumentWithMultipleProfiles(context, url)
    
    
    def doMaskImageWithMask(context):
        theImageToMaskURL = GetURL(kOtherColorImage)
        theMaskingImageURL = GetURL(kMaskingImage)
    
        if theImageToMaskURL is not None and theMaskingImageURL is not None:
            ImageMasking.doMaskImageWithMaskFromURL(
                context,
                theImageToMaskURL,
                RAW_IMAGE_WIDTH,
                RAW_IMAGE_HEIGHT,
                8,
                theMaskingImageURL,
                MASKING_IMAGE_WIDTH,
                MASKING_IMAGE_HEIGHT,
            )
    
    
    def doMaskImageWithGrayImage(context):
        theImageToMaskURL = GetURL(kOtherColorImage)
        theMaskingImageURL = GetURL(kMaskingImage)
    
        if theImageToMaskURL is not None and theMaskingImageURL is not None:
            ImageMasking.doMaskImageWithGrayImageFromURL(
                context,
                theImageToMaskURL,
                RAW_IMAGE_WIDTH,
                RAW_IMAGE_HEIGHT,
                8,
                theMaskingImageURL,
                MASKING_IMAGE_WIDTH,
                MASKING_IMAGE_HEIGHT,
            )
    
    
    def doImageMaskedWithColor(context):
        url = GetURL(kOtherColorImage)
    
        if url is not None:
            ImageMasking.doMaskImageWithColorFromURL(
                context, url, RAW_IMAGE_WIDTH, RAW_IMAGE_HEIGHT, True
            )
    
    
    def exportImageMaskedWithImage(context):
        theImageToMaskURL = GetURL(kOtherColorImage)
        theMaskingImageURL = GetURL(kMaskingImage)
    
        if theImageToMaskURL is not None and theMaskingImageURL is not None:
            ImageMasking.exportImageWithMaskFromURLWithDestination(
                context,
                theImageToMaskURL,
                RAW_IMAGE_WIDTH,
                RAW_IMAGE_HEIGHT,
                8,
                theMaskingImageURL,
                MASKING_IMAGE_WIDTH,
                MASKING_IMAGE_HEIGHT,
            )
    
    
    def doClipMask(context):
        theMaskingImageURL = GetURL(kMaskingImage)
    
        if theMaskingImageURL is not None:
            ImageMasking.drawWithClippingMask(
                context, theMaskingImageURL, MASKING_IMAGE_WIDTH, MASKING_IMAGE_HEIGHT
            )
    
    
    def tilePDFDocument(context, offscreenType):
        url = GetURL(kCatPDF)
    
        if url is not None:
            if offscreenType == noOffScreen:
                BitmapContext.TilePDFNoBuffer(context, url)
            elif offscreenType == bitmapOffScreen:
                BitmapContext.TilePDFWithOffscreenBitmap(context, url)
            else:
                BitmapContext.TilePDFWithCGLayer(context, url)
    
    
    def doCompatibleEPSDrawing(context):
        ourEPSurl = GetURL(kOurEPS)
    
        if ourEPSurl is not None:
            EPSPrinting.drawEPSDataImage(context, ourEPSurl)
    
    
    def DispatchDrawing(context, drawingType):
        """Drawing dispatcher"""
        if drawingType == UIHandling.kHICommandSimpleRect:
            DrawingBasics.doSimpleRect(context)
    
        elif drawingType == UIHandling.kHICommandStrokedRect:
            DrawingBasics.doStrokedRect(context)
    
        elif drawingType == UIHandling.kHICommandStrokedAndFilledRect:
            DrawingBasics.doStrokedAndFilledRect(context)
    
        elif drawingType == UIHandling.kHICommandPathRects:
            DrawingBasics.doPathRects(context)
    
        elif drawingType == UIHandling.kHICommandAlphaRects:
            DrawingBasics.doAlphaRects(context)
    
        elif drawingType == UIHandling.kHICommandDashed:
            DrawingBasics.doDashedLines(context)
    
        elif drawingType == UIHandling.kHICommandSimpleClip:
            DrawingBasics.doClippedCircle(context)
    
        elif drawingType == UIHandling.kHICommandPDFDoc:
            callPDFDrawProc(context, DrawingBasics.doPDFDocument, kCatPDF)
    
        elif drawingType == UIHandling.kHICommandRotatedEllipses:
            CoordinateSystem.doRotatedEllipses(context)
    
        elif drawingType == UIHandling.kHICommandDrawSkewCoordinates:
            CoordinateSystem.drawSkewedCoordinateSystem(context)
    
        elif drawingType == UIHandling.kHICommandBezierEgg:
            PathDrawing.doEgg(context)
    
        elif drawingType == UIHandling.kHICommandRoundedRects:
            PathDrawing.doRoundedRects(context)
    
        elif drawingType == UIHandling.kHICommandStrokeWithCTM:
            PathDrawing.doStrokeWithCTM(context)
    
        elif drawingType == UIHandling.kHICommandRotatedEllipsesWithCGPath:
            PathDrawing.doRotatedEllipsesWithCGPath(context)
    
        elif drawingType == UIHandling.kHICommandPixelAligned:
            PathDrawing.doPixelAlignedFillAndStroke(context)
    
        elif drawingType == UIHandling.kHICommandDeviceFillAndStrokeColor:
            ColorAndGState.doColorSpaceFillAndStroke(context)
    
        elif drawingType == UIHandling.kHICommandCLUTDrawGraphics:
            ColorAndGState.doIndexedColorDrawGraphics(context)
    
        elif drawingType == UIHandling.kHICommandDrawWithGlobalAlpha:
            ColorAndGState.drawWithGlobalAlpha(context)
    
        elif drawingType == UIHandling.kHICommandDrawWithBlendMode:
            callPDFDrawProc(
                context, ColorAndGState.drawWithColorBlendMode, kPDFForBlendMode
            )
    
        elif drawingType == UIHandling.kHICommandDrawWithColorRefs:
            ColorAndGState.drawWithColorRefs(context)
    
        elif drawingType == UIHandling.kHICommandFunctionsHaveOwnGSave:
            ColorAndGState.doClippedEllipse(context)
    
        elif drawingType == UIHandling.kHICommandDrawJPEGImage:
            doDrawJPEGFile(context)
    
        elif drawingType == UIHandling.kHICommandColorImageFromFile:
            doRawImageFileWithURL(context)
    
        elif drawingType == UIHandling.kHICommandColorImageFromData:
            Images.doColorRampImage(context)
    
        elif drawingType == UIHandling.kHICommandColorImageFromCallbacks:
            doRawImageFileWithCallbacks(context)
    
        elif drawingType == UIHandling.kHICommandGrayRamp:
            Images.doGrayRamp(context)
    
        elif drawingType == UIHandling.kHICommandDrawWithCGImageSource:
            doDrawImageWithCGImageSource(context)
    
        elif drawingType == UIHandling.kHICommandDrawWithCGImageSourceIncremental:
            doIncrementalImage(context)
    
        elif drawingType == UIHandling.kHICommandDrawWithQuickTime:
            doQTImage(context)
    
        elif drawingType == UIHandling.kHICommandSubstituteImageProfile:
            doJPEGDocumentWithMultipleProfiles(context)
    
        elif drawingType == UIHandling.kHICommandDoSubImage:
            Images.doColorRampSubImage(context)
    
        elif drawingType == UIHandling.kHICommandExportWithQuickTime:
            Images.exportColorRampImageWithQT(context)
    
        elif drawingType == UIHandling.kHICommandMaskTurkeyImage:
            ImageMasking.doOneBitMaskImages(context)
    
        elif drawingType == UIHandling.kHICommandImageMaskedWithMask:
            doMaskImageWithMask(context)
    
        elif drawingType == UIHandling.kHICommandImageMaskedWithGrayImage:
            doMaskImageWithGrayImage(context)
    
        elif drawingType == UIHandling.kHICommandMaskImageWithColor:
            doImageMaskedWithColor(context)
    
        elif drawingType == UIHandling.kHICommandClipToMask:
            doClipMask(context)
    
        elif drawingType == UIHandling.kHICommandExportWithCGImageDestination:
            exportImageMaskedWithImage(context)
    
        elif drawingType == UIHandling.kHICommandSimpleCGLayer:
            BitmapContext.doSimpleCGLayer(context)
    
        elif drawingType == UIHandling.kHICommandAlphaOnlyContext:
            BitmapContext.doAlphaOnlyContext(context)
    
        elif drawingType == UIHandling.kHICommandDrawNoOffScreenImage:
            tilePDFDocument(context, noOffScreen)
    
        elif drawingType == UIHandling.kHICommandDrawOffScreenImage:
            tilePDFDocument(context, bitmapOffScreen)
    
        elif drawingType == UIHandling.kHICommandDrawWithLayer:
            tilePDFDocument(context, layerOffScreen)
    
        elif drawingType == UIHandling.kHICommandQuartzRomanText:
            QuartzTextDrawing.drawQuartzRomanText(context)
    
        elif drawingType == UIHandling.kHICommandQuartzTextModes:
            QuartzTextDrawing.drawQuartzTextWithTextModes(context)
    
        elif drawingType == UIHandling.kHICommandQuartzTextMatrix:
            QuartzTextDrawing.drawQuartzTextWithTextMatrix(context)
    
        elif drawingType == UIHandling.kHICommandSimplePattern:
            PatternDrawing.doRedBlackCheckerboard(context)
    
        elif drawingType == UIHandling.kHICommandPatternPhase:
            PatternDrawing.doPatternPhase(context)
    
        elif drawingType == UIHandling.kHICommandPatternMatrix:
            PatternDrawing.doPatternMatrix(context)
    
        elif drawingType == UIHandling.kHICommandUncoloredPattern:
            PatternDrawing.doStencilPattern(context)
    
        elif drawingType == UIHandling.kHICommandDrawWithPDFPattern:
            callPDFDrawProc(context, PatternDrawing.drawWithPDFPattern, kCatPDF)
    
        elif drawingType == UIHandling.kHICommandSimpleShadow:
            ShadowsAndTransparencyLayers.drawSimpleShadow(context)
    
        elif drawingType == UIHandling.kHICommandShadowScaling:
            ShadowsAndTransparencyLayers.doShadowScaling(context)
    
        elif drawingType == UIHandling.kHICommandShadowProblems:
            ShadowsAndTransparencyLayers.showComplexShadowIssues(context)
    
        elif drawingType == UIHandling.kHICommandComplexShadow:
            ShadowsAndTransparencyLayers.showComplexShadow(context)
    
        elif drawingType == UIHandling.kHICommandMultipleShapeComposite:
            ShadowsAndTransparencyLayers.doLayerCompositing(context)
    
        elif drawingType == UIHandling.kHICommandFillAndStrokeWithShadow:
            ShadowsAndTransparencyLayers.drawFillAndStrokeWithShadow(context)
    
        elif drawingType == UIHandling.kHICommandPDFDocumentShadow:
            callPDFDrawProc(
                context, ShadowsAndTransparencyLayers.shadowPDFDocument, kCatPDF
            )
    
        elif drawingType == UIHandling.kHICommandSimpleAxialShading:
            Shadings.doSimpleAxialShading(context)
    
        elif drawingType == UIHandling.kHICommandExampleAxialShadings:
            Shadings.doExampleAxialShading(context)
    
        elif drawingType == UIHandling.kHICommandSimpleRadialShading:
            Shadings.doSimpleRadialShading(context)
    
        elif drawingType == UIHandling.kHICommandExampleRadialShadings:
            Shadings.doExampleRadialShadings(context)
    
        elif drawingType == UIHandling.kHICommandEllipseShading:
            Shadings.doEllipseShading(context)
    
        elif drawingType == UIHandling.kHICommandDoCompatibleEPS:
            doCompatibleEPSDrawing(context)

.. rst-class:: tabbertab

BitmapContext.py
................

.. sourcecode:: python

    import AppDrawing
    import DrawingBasics
    import LaunchServices
    import Quartz
    import Utilities
    
    # import Carbon.QT
    
    
    BEST_BYTE_ALIGNMENT = 16
    
    
    def COMPUTE_BEST_BYTES_PER_ROW(bpr):
        return (int(bpr) + BEST_BYTE_ALIGNMENT - 1) & ~(BEST_BYTE_ALIGNMENT - 1)
    
    
    # We need to ensure that the raster data stays alive until we clean up
    # the context, store it here.
    _rasterDataForContext = {}
    
    
    def createRGBBitmapContext(
        width, height, wantDisplayColorSpace, needsTransparentBitmap
    ):
        # This routine allocates data for a pixel array that contains width*height
        # pixels where each pixel is 4 bytes. The format is 8-bit ARGB or XRGB, depending on
        # whether needsTransparentBitmap is true. In order to get the recommended
        # pixel alignment, the bytesPerRow is rounded up to the nearest multiple
        # of BEST_BYTE_ALIGNMENT bytes.
    
        # Minimum bytes per row is 4 bytes per sample * number of samples.
        bytesPerRow = width * 4
        # Round to nearest multiple of BEST_BYTE_ALIGNMENT.
        bytesPerRow = COMPUTE_BEST_BYTES_PER_ROW(bytesPerRow)
    
        # Allocate the data for the raster. The total amount of data is bytesPerRow
        # times the number of rows. The function 'calloc' is used so that the
        # memory is initialized to 0.
        try:
            rasterData = bytearray(int(bytesPerRow * height))
        except MemoryError:
            return None
    
        # The wantDisplayColorSpace argument passed to the function determines
        # whether or not to use the display color space or the generic calibrated
        # RGB color space. The needsTransparentBitmap argument determines whether
        # create a context that records alpha or not.
        if wantDisplayColorSpace:
            cs = Utilities.getTheDisplayColorSpace()
        else:
            cs = Utilities.getTheCalibratedRGBColorSpace()
    
        if needsTransparentBitmap:
            transparency = Quartz.kCGImageAlphaPremultipliedFirst
        else:
            transparency = Quartz.kCGImageAlphaPremultipliedFirst
    
        context = Quartz.CGBitmapContextCreate(
            rasterData, width, height, 8, bytesPerRow, cs, transparency
        )
        if context is None:
            return None
    
        _rasterDataForContext[context] = rasterData
    
        # Either clear the rect or paint with opaque white, depending on
        # the needs of the caller.
        if needsTransparentBitmap:
            # Clear the context bits so they are transparent.
            Quartz.CGContextClearRect(context, Quartz.CGRectMake(0, 0, width, height))
    
        else:
            # Since the drawing destination is opaque, first paint
            # the context bits to white.
            Quartz.CGContextSaveGState(context)
            Quartz.CGContextSetFillColorWithColor(
                context, Utilities.getRGBOpaqueWhiteColor()
            )
            Quartz.CGContextFillRect(context, Quartz.CGRectMake(0, 0, width, height))
            Quartz.CGContextRestoreGState(context)
    
        return context
    
    
    def myCGContextGetBitmapInfo(c):
        if hasattr(Quartz, "CGBitmapContextGetBitmapInfo"):
            return Quartz.CGBitmapContextGetBitmapInfo(c)
        else:
            return Quartz.CGBitmapContextGetAlphaInfo(c)
    
    
    # createImageFromBitmapContext creates a CGImageRef
    # from a bitmap context. Calling this routine
    # transfers 'ownership' of the raster data
    # in the bitmap context, to the image. If the
    # image can't be created, this routine frees
    # the memory associated with the raster.
    def createImageFromBitmapContext(c):
        rasterData = _rasterDataForContext[c]
        # We own the data, hence remove from the mapping
        del _rasterDataForContext[c]
    
        imageDataSize = Quartz.CGBitmapContextGetBytesPerRow(
            c
        ) * Quartz.CGBitmapContextGetHeight(c)
    
        if rasterData is None:
            print("Context is not a bitmap context!")
    
        # Create the data provider from the image data
        dataProvider = Quartz.CGDataProviderCreateWithData(
            None, rasterData, imageDataSize, None
        )
        if dataProvider is None:
            print("Couldn't create data provider!")
            return None
    
        # Now create the image. The parameters for the image closely match
        # the parameters of the bitmap context. This code uses a NULL
        # decode array and shouldInterpolate is true.
        image = Quartz.CGImageCreate(
            Quartz.CGBitmapContextGetWidth(c),
            Quartz.CGBitmapContextGetHeight(c),
            Quartz.CGBitmapContextGetBitsPerComponent(c),
            Quartz.CGBitmapContextGetBitsPerPixel(c),
            Quartz.CGBitmapContextGetBytesPerRow(c),
            Quartz.CGBitmapContextGetColorSpace(c),
            myCGContextGetBitmapInfo(c),
            dataProvider,
            None,
            True,
            Quartz.kCGRenderingIntentDefault,
        )
    
        if image is None:
            print("Couldn't create image!")
            return None
        return image
    
    
    def exportCGImageToFileWithQT(image, url, outputFormat, dpi):
        """
        Export an image using QuickTime API's.
    
        This function can't possibly work, as the relevant APIs aren't available
        through MacPython. The code below is mostly there in case someone fixes
        the MacPython QuickTime bindings.
        """
        # XXX: Finish
        return
    
        if outputFormat.lower() == LaunchServices.kUTTypeTIFF.lower():
            imageExportType = LaunchServices.kQTFileTypeTIFF
    
        elif outputFormat.lower() == LaunchServices.kUTTypePNG.lower():
            imageExportType = LaunchServices.kQTFileTypePNG
    
        elif outputFormat.lower() == LaunchServices.kUTTypeJPEG.lower():
            imageExportType = LaunchServices.kQTFileTypeJPEG
    
        else:
            print(f"Requested image export format {outputFormat!r} unsupported")
            return
    
            result, dataRef, dataRefType = Quartz.QTNewDataReferenceFromCFURL(
                url, 0, None, None
            )
            if result == 0:
                result, graphicsExporter = Quartz.OpenADefaultComponent(
                    Quartz.GraphicsExporterComponentType,
                    imageExportType,
                    graphicsExporter,  # noqa: F821
                )
                if result == 0:
                    result = Quartz.GraphicsExportSetInputCGImage(graphicsExporter, image)
                    if result == 0:
                        result = Quartz.GraphicsExportSetResolution(
                            graphicsExporter,
                            Quartz.FloatToFixed(dpi),
                            Quartz.FloatToFixed(dpi),
                        )
                    if result == 0:
                        result = Quartz.GraphicsExportSetOutputDataReference(
                            graphicsExporter, dataRef, dataRefType
                        )
                    if result == 0:
                        result, sizeWritten = Quartz.GraphicsExportDoExport(
                            graphicsExporter, None
                        )
                    Quartz.CloseComponent(graphicsExporter)
    
            if dataRef:
                Quartz.DisposeHandle(dataRef)
    
            if result:
                print("QT export got bad result = %d!" % (result,))
    
    
    def exportCGImageToFileWithDestination(image, url, outputFormat, dpi):
        # Create an image destination at the supplied URL that
        # corresponds to the output image format. The destination will
        # only contain 1 image.
        imageDestination = Quartz.CGImageDestinationCreateWithURL(
            url, outputFormat, 1, None
        )
    
        if imageDestination is None:
            print("Couldn't create image destination!")
            return
    
        # Create an options dictionary with the X&Y resolution of the image
        options = {
            Quartz.kCGImagePropertyDPIWidth: dpi,
            Quartz.kCGImagePropertyDPIHeight: dpi,
        }
    
        # Add the image with the options dictionary to the destination.
        Quartz.CGImageDestinationAddImage(imageDestination, image, options)
    
        # When all the images are added to the destination, finalize it.
        Quartz.CGImageDestinationFinalize(imageDestination)
    
    
    def MakeImageDocument(url, imageType, exportInfo):
        # First make a bitmap context for a US Letter size
        # raster at the requested resolution.
        dpi = exportInfo.dpi
        width = int(8.5 * dpi)
        height = int(11 * dpi)
    
        # For JPEG output type the bitmap should not be transparent. If other types are added that
        # do not support transparency, this code should be updated to check for those types as well.
        needTransparentBitmap = imageType.lower() != LaunchServices.kUTTypeJPEG.lower()
    
        # Create an RGB Bitmap context using the generic calibrated RGB color space
        # instead of the display color space.
        useDisplayColorSpace = False
        c = createRGBBitmapContext(
            width, height, useDisplayColorSpace, needTransparentBitmap
        )
    
        if c is None:
            print("Couldn't make destination bitmap context")
            return -1
    
        # Scale the coordinate system based on the resolution in dots per inch.
        Quartz.CGContextScaleCTM(c, dpi / 72, dpi / 72)
    
        # Set the font smoothing parameter to false since it's better to
        # draw any text without special LCD text rendering when creating
        # rendered data for export.
        if hasattr(Quartz, "CGContextSetShouldSmoothFonts"):
            Quartz.CGContextSetShouldSmoothFonts(c, False)
    
        # Set the scaling factor for shadows. This is a hack so that
        # drawing code that needs to know the scaling factor can
        # obtain it. Better would be that DispatchDrawing and the code
        # it calls would take this scaling factor as a parameter.
        Utilities.setScalingFactor(dpi / 72)
    
        # Draw into that raster...
        AppDrawing.DispatchDrawing(c, exportInfo.command)
    
        # Set the scaling factor back to 1.0.
        Utilities.setScalingFactor(1.0)
    
        # Create an image from the raster data. Calling
        # createImageFromBitmapContext gives up ownership
        # of the raster data used by the context.
        image = createImageFromBitmapContext(c)
    
        # Release the context now that the image is created.
        del c
    
        if image is None:
            # Users of this code should update this to be an error code they find useful.
            return -1
    
        # Now export the image.
        if exportInfo.useQTForExport:
            exportCGImageToFileWithQT(image, url, imageType, exportInfo.dpi)
        else:
            exportCGImageToFileWithDestination(image, url, imageType, exportInfo.dpi)
    
    
    def MakeTIFFDocument(url, exportInfo):
        return MakeImageDocument(url, LaunchServices.kUTTypeTIFF, exportInfo)
    
    
    def MakePNGDocument(url, exportInfo):
        return MakeImageDocument(url, LaunchServices.kUTTypePNG, exportInfo)
    
    
    def MakeJPEGDocument(url, exportInfo):
        return MakeImageDocument(url, LaunchServices.kUTTypeJPEG, exportInfo)
    
    
    def createCGLayerForDrawing(c):
        rect = Quartz.CGRectMake(0, 0, 50, 50)
    
        # Make the layer the size of the rectangle that
        # this code draws into the layer.
        layerSize = rect.size
    
        # Create the layer to draw into.
        layer = Quartz.CGLayerCreateWithContext(c, layerSize, None)
        if layer is None:
            return None
    
        # Get the context corresponding to the layer.
        layerContext = Quartz.CGLayerGetContext(layer)
        if layerContext is None:
            return None
    
        # $ Set the fill color to opaque black.
        Quartz.CGContextSetFillColorWithColor(
            layerContext, Utilities.getRGBOpaqueBlackColor()
        )
    
        # Draw the content into the layer.
        Quartz.CGContextFillRect(layerContext, rect)
    
        # Now the layer has the contents needed.
        return layer
    
    
    def doSimpleCGLayer(context):
        # Create the layer.
        layer = createCGLayerForDrawing(context)
    
        if layer is None:
            print("Couldn't create layer!")
            return
    
        # Get the size of the layer created.
        s = Quartz.CGLayerGetSize(layer)
    
        # Clip to a rect that corresponds to
        # a grid of 8x8 layer objects.
        Quartz.CGContextClipToRect(
            context, Quartz.CGRectMake(0, 0, 8 * s.width, 8 * s.height)
        )
    
        # Paint 8 rows of layer objects.
        for j in range(8):
            Quartz.CGContextSaveGState(context)
            # Paint 4 columns of layer objects, moving
            # across the drawing canvas by skipping a
            # square on the grid each time across.
            for _ in range(4):
                # Draw the layer at the current origin.
                Quartz.CGContextDrawLayerAtPoint(context, Quartz.CGPointZero, layer)
                # Translate across two layer widths.
                Quartz.CGContextTranslateCTM(context, 2 * s.width, 0)
    
            Quartz.CGContextRestoreGState(context)
            # Translate to the left one layer width on
            # even loop counts and to the right one
            # layer width on odd loop counts. Each
            # time through the outer loop, translate up
            # one layer height.
            if j % 2:
                Quartz.CGContextTranslateCTM(context, s.width, s.height)
            else:
                Quartz.CGContextTranslateCTM(context, -s.width, s.height)
    
    
    def createAlphaOnlyContext(width, height):
        # This routine allocates data for a pixel array that contains
        # width*height pixels, each pixel is 1 byte. The format is
        # 8 bits per pixel, where the data is the alpha value of the pixel.
    
        # Minimum bytes per row is 1 byte per sample * number of samples.
        bytesPerRow = width
        # Round to nearest multiple of BEST_BYTE_ALIGNMENT.
        bytesPerRow = COMPUTE_BEST_BYTES_PER_ROW(bytesPerRow)
    
        # Allocate the data for the raster. The total amount of data is bytesPerRow
        # // times the number of rows. The function 'calloc' is used so that the
        # // memory is initialized to 0.
        try:
            rasterData = bytearray(bytesPerRow * height)
        except MemoryError:
            return None
    
        # This type of context is only available in Panther and later, otherwise
        # this fails and returns a NULL context. The color space for an alpha
        # // only context is NULL and the BitmapInfo value is kCGImageAlphaOnly.
        context = Quartz.CGBitmapContextCreate(
            rasterData, width, height, 8, bytesPerRow, None, Quartz.kCGImageAlphaOnly
        )
        if context is None:
            print("Couldn't create the context!")
            return None
    
        _rasterDataForContext[context] = rasterData
    
        # Clear the context bits so they are initially transparent.
        Quartz.CGContextClearRect(context, Quartz.CGRectMake(0, 0, width, height))
    
        return context
    
    
    # createMaskFromAlphaOnlyContext creates a CGImageRef
    # from an alpha-only bitmap context. Calling this routine
    # transfers 'ownership' of the raster data in the bitmap
    # context, to the image. If the image can't be created, this
    # routine frees the memory associated with the raster.
    
    
    def createMaskFromAlphaOnlyContext(alphaContext):
        rasterData = _rasterDataForContext[alphaContext]
        # We own the data, hence remove from the mapping
        del _rasterDataForContext[alphaContext]
    
        imageDataSize = Quartz.CGBitmapContextGetBytesPerRow(
            alphaContext
        ) * Quartz.CGBitmapContextGetHeight(alphaContext)
        invertDecode = [1.0, 0.0]
    
        # Create the data provider from the image data.
        dataProvider = Quartz.CGDataProviderCreateWithData(
            None, rasterData, imageDataSize, None
        )
    
        if dataProvider is None:
            print("Couldn't create data provider!")
            return None
    
        mask = Quartz.CGImageMaskCreate(
            Quartz.CGBitmapContextGetWidth(alphaContext),
            Quartz.CGBitmapContextGetHeight(alphaContext),
            Quartz.CGBitmapContextGetBitsPerComponent(alphaContext),
            Quartz.CGBitmapContextGetBitsPerPixel(alphaContext),
            Quartz.CGBitmapContextGetBytesPerRow(alphaContext),
            dataProvider,
            # The decode is an inverted decode since a mask has the opposite
            # sense than alpha, i.e. 0 in a mask paints 100% and 1 in a mask
            # paints nothing.
            invertDecode,
            True,
        )
    
        if mask is None:
            print("Couldn't create image mask!")
            return None
    
        return mask
    
    
    def doAlphaOnlyContext(context):
        # This code is going to capture the alpha coverage
        # of the drawing done by the doAlphaRects routine.
        # The value passed here as the width and height is
        # the size of the bounding rectangle of that drawing.
        width = 520
        height = 400
        alphaContext = createAlphaOnlyContext(width, height)
        if context is None:
            print("Couldn't create the alpha-only context!")
            return
    
        # Draw the content to the alpha-only context, capturing
        # the alpha coverage. The doAlphaRects routine paints
        # a series of translucent red rectangles.
        DrawingBasics.doAlphaRects(alphaContext)
    
        # Finished drawing to the context and now the raster contains
        # the alpha data captured from the drawing. Create
        # the mask from the data in the context.
        mask = createMaskFromAlphaOnlyContext(alphaContext)
        # This code is now finished with the context so it can
        # release it.
        del alphaContext
    
        if mask is None:
            return
    
        # Set the fill color space.
        Quartz.CGContextSetFillColorSpace(
            context, Utilities.getTheCalibratedRGBColorSpace()
        )
        opaqueBlue = (0.11, 0.208, 0.451, 1.0)
        # Set the painting color to opaque blue.
        Quartz.CGContextSetFillColor(context, opaqueBlue)
        # Draw the mask, painting the mask with blue. This colorizes
        # the image to blue and it is as if we painted the
        # alpha rects with blue instead of red.
        Quartz.CGContextDrawImage(context, Quartz.CGRectMake(0, 0, width, height), mask)
    
    
    _pdfDoc = None
    _pdfURL = None
    _width = 0
    _height = 0
    
    
    def getThePDFDoc(url):
        """
        This function caches a CGPDFDocumentRef for
        the most recently requested PDF document.
        """
        global _pdfDoc, _pdfURL, _width, _height
    
        if url is None:
            return None, 0, 0
    
        # See whether to update the cached PDF document.
        if _pdfDoc is None or url != _pdfURL:
            # Release any cached document or URL.
            _pdfDoc = Quartz.CGPDFDocumentCreateWithURL(url)
            if _pdfDoc is not None:
                pdfMediaRect = Quartz.CGPDFDocumentGetMediaBox(_pdfDoc, 1)
                _width = pdfMediaRect.size.width
                _height = pdfMediaRect.size.height
                # Keep the URL of the PDF file being cached.
                _pdfURL = url
    
            else:
                _pdfURL = None
    
        if _pdfDoc is not None:
            return _pdfDoc, _width, _height
    
        else:
            return None, 0, 0
    
    
    # Defining this scales the content down by 1/3.
    DOSCALING = True
    
    
    def TilePDFNoBuffer(context, url):
        # The amount of area to tile should really be based on the
        # window/document. Here it is hard coded to a US Letter
        # size document. This may draw too many or too few tiles
        # for the area actually being filled.
        fillwidth = 612.0
        fillheight = 792.0
        extraOffset = 6.0
        pdfDoc, tileX, tileY = getThePDFDoc(url)
        if pdfDoc is None:
            print("Couldn't get the PDF document!")
            return
    
        if DOSCALING:
            # Make the tiles 1/3 the size of the PDF document.
            tileX /= 3
            tileY /= 3
            extraOffset /= 3
    
        # Space the tiles by the tile width and height
        # plus extraOffset units in each dimension.
        tileOffsetX = extraOffset + tileX
        tileOffsetY = extraOffset + tileY
    
        # Tile the PDF document.
        for h in range(0, int(fillheight), int(tileOffsetY)):
            for w in range(0, int(fillwidth), int(tileOffsetX)):
                Quartz.CGContextDrawPDFDocument(
                    context, Quartz.CGRectMake(w, h, tileX, tileY), pdfDoc, 1
                )
    
    
    def TilePDFWithOffscreenBitmap(context, url):
        # Again this should really be computed based on
        # the area intended to be tiled.
        fillwidth = 612.0
        fillheight = 792.0
        extraOffset = 6.0
    
        pdfDoc, tileX, tileY = getThePDFDoc(url)
        if pdfDoc is None:
            print("Couldn't get the PDF document")
            return
    
        if DOSCALING:
            # Make the tiles 1/3 the size of the PDF document.
            tileX /= 3
            tileY /= 3
            extraOffset /= 3
    
        # Space the tiles by the tile width and height
        # plus extraOffset units in each dimension.
        tileOffsetX = extraOffset + tileX
        tileOffsetY = extraOffset + tileY
    
        # Since the bitmap context is for use with the display
        # and should capture alpha, these are the values
        # to pass to createRGBBitmapContext.
        useDisplayColorSpace = True
        needTransparentBitmap = True
        bitmapContext = createRGBBitmapContext(
            tileX, tileY, useDisplayColorSpace, needTransparentBitmap
        )
        if bitmapContext is None:
            print("Couldn't create bitmap context!")
            return
    
        # Draw the PDF document one time into the bitmap context.
        Quartz.CGContextDrawPDFDocument(
            bitmapContext, Quartz.CGRectMake(0, 0, tileX, tileY), pdfDoc, 1
        )
    
        # Create an image from the raster data. Calling
        # createImageFromBitmapContext gives up ownership
        # of the raster data used by the context.
        image = createImageFromBitmapContext(bitmapContext)
    
        # Release the context now that the image is created.
        del bitmapContext
    
        if image is None:
            return
    
        # Now tile the image.
        for h in range(0, int(fillheight), int(tileOffsetY)):
            for w in range(0, int(fillwidth), int(tileOffsetX)):
                Quartz.CGContextDrawImage(
                    context, Quartz.CGRectMake(w, h, tileX, tileY), image
                )
    
    
    def createLayerWithImageForContext(c, url):
        layerSize = Quartz.CGSize()
        pdfDoc, layerSize.width, layerSize.height = getThePDFDoc(url)
        if pdfDoc is None:
            return None
    
        if DOSCALING:
            # Make the layer 1/3 the size of the PDF document.
            layerSize.width /= 3
            layerSize.height /= 3
    
        # Create the layer to draw into.
        layer = Quartz.CGLayerCreateWithContext(c, layerSize, None)
        if layer is None:
            return None
    
        # Get the context corresponding to the layer. Note
        # that this is a 'Get' function so the code must
        # not release the context.
        layerContext = Quartz.CGLayerGetContext(layer)
        if layerContext is None:
            return None
    
        # Draw the PDF document into the layer.
        Quartz.CGContextDrawPDFDocument(
            layerContext,
            Quartz.CGRectMake(0, 0, layerSize.width, layerSize.height),
            pdfDoc,
            1,
        )
    
        # Now the layer has the contents needed.
        return layer
    
    
    def TilePDFWithCGLayer(context, url):
        # Again this should really be computed based on
        # the area intended to be tiled.
        fillwidth = 612.0
        fillheight = 792.0
        layer = createLayerWithImageForContext(context, url)
        if layer is None:
            print("Couldn't create the layer!")
            return
    
        # Compute the tile size and offset.
        s = Quartz.CGLayerGetSize(layer)
        tileX = s.width
        tileY = s.height
    
        if DOSCALING:
            # Space the tiles by the tile width and height
            # plus an extra 2 units in each dimension.
            tileOffsetX = 2.0 + tileX
            tileOffsetY = 2.0 + tileY
        else:
            # Add 6 units to the offset in each direction
            # if there is no scaling of the source PDF document.
            tileOffsetX = 6.0 + tileX
            tileOffsetY = 6.0 + tileY
    
        # Now draw the contents of the layer to the context.
        # The layer is drawn at its true size (the size of
        # the tile) with its origin located at the corner
        # of each tile.
        for h in range(0, int(fillheight), int(tileOffsetY)):
            for w in range(0, int(fillwidth), int(tileOffsetX)):
                Quartz.CGContextDrawLayerAtPoint(context, Quartz.CGPointMake(w, h), layer)

.. rst-class:: tabbertab

ColorAndGState.py
.................

.. sourcecode:: python

    import array
    
    import Quartz
    import Utilities
    
    
    def doColorSpaceFillAndStroke(context):
        theColorSpace = Utilities.getTheCalibratedRGBColorSpace()
        opaqueRed = (0.663, 0.0, 0.031, 1.0)  # red,green,blue,alpha
        aBlue = (0.482, 0.62, 0.871, 1.0)  # red,green,blue,alpha
    
        # Set the fill color space to be the generic calibrated RGB color space.
        Quartz.CGContextSetFillColorSpace(context, theColorSpace)
        # Set the fill color to opaque red. The number of elements in the
        # array passed to this function must be the number of color
        # components in the current fill color space plus 1 for alpha.
        Quartz.CGContextSetFillColor(context, opaqueRed)
    
        # Set the stroke color space to be the generic calibrated RGB color space.
        Quartz.CGContextSetStrokeColorSpace(context, theColorSpace)
        # Set the stroke color to opaque blue. The number of elements
        # in the array passed to this function must be the number of color
        # components in the current stroke color space plus 1 for alpha.
        Quartz.CGContextSetStrokeColor(context, aBlue)
    
        Quartz.CGContextSetLineWidth(context, 8.0)
        # Rectangle 1.
        Quartz.CGContextBeginPath(context)
        Quartz.CGContextAddRect(context, Quartz.CGRectMake(20.0, 20.0, 100.0, 100.0))
        Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
    
        # Continue to use the stroke colorspace already set
        # but change the stroke alpha value to a semitransparent blue.
        aBlue = list(aBlue)
        aBlue[3] = 0.5
        Quartz.CGContextSetStrokeColor(context, aBlue)
        # Rectangle 2.
        Quartz.CGContextBeginPath(context)
        Quartz.CGContextAddRect(context, Quartz.CGRectMake(140.0, 20.0, 100.0, 100.0))
        Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
    
        # Don't release the color space since this routine
        # didn't create it.
    
    
    _opaqueRedColor = None
    _opaqueBlueColor = None
    _transparentBlueColor = None
    
    
    def drawWithColorRefs(context):
        global _opaqueRedColor
        global _opaqueBlueColor
        global _transparentBlueColor
    
        # Initialize the CGColorRefs if necessary
        if _opaqueRedColor is None:
            # Initialize the color array to an opaque red
            # in the generic calibrated RGB color space.
            color = (0.663, 0.0, 0.031, 1.0)
            theColorSpace = Utilities.getTheCalibratedRGBColorSpace()
            # Create a CGColorRef for opaque red.
            _opaqueRedColor = Quartz.CGColorCreate(theColorSpace, color)
            # Make the color array correspond to an opaque blue color.
            color = (0.482, 0.62, 0.87, 1.0)
            # Create another Quartz.CGColorRef for opaque blue.
            _opaqueBlueColor = Quartz.CGColorCreate(theColorSpace, color)
            # Create a new CGColorRef from the opaqueBlue CGColorRef
            # but with a different alpha value.
            _transparentBlueColor = Quartz.CGColorCreateCopyWithAlpha(_opaqueBlueColor, 0.5)
            if (
                _opaqueRedColor is None
                or _opaqueBlueColor is None
                or _transparentBlueColor is None
            ):
                print("Couldn't create one of the CGColorRefs!!!")
                return
    
        # Set the fill color to the opaque red CGColor object.
        Quartz.CGContextSetFillColorWithColor(context, _opaqueRedColor)
        # Set the stroke color to the opaque blue CGColor object.
        Quartz.CGContextSetStrokeColorWithColor(context, _opaqueBlueColor)
    
        Quartz.CGContextSetLineWidth(context, 8.0)
        # Draw the first rectangle.
        Quartz.CGContextBeginPath(context)
        Quartz.CGContextAddRect(context, Quartz.CGRectMake(20.0, 20.0, 100.0, 100.0))
        Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
    
        # Set the stroke color to be that of the transparent blue
        # CGColor object.
        Quartz.CGContextSetStrokeColorWithColor(context, _transparentBlueColor)
        # Draw a second rectangle to the right of the first one.
        Quartz.CGContextBeginPath(context)
        Quartz.CGContextAddRect(context, Quartz.CGRectMake(140.0, 20.0, 100.0, 100.0))
        Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
    
    
    def doIndexedColorDrawGraphics(context):
        theBaseRGBSpace = Utilities.getTheCalibratedRGBColorSpace()
        lookupTable = array.array("B", (0,) * 6)
        opaqueRed = (0, 1)  # index, alpha
        aBlue = (1, 1)  # index, alpha
    
        # Set the first 3 values in the lookup table to a red of
        # 169/255 = 0.663, no green, and blue = 8/255 = 0.031. This makes
        # the first entry in the lookup table a shade of red.
        lookupTable[0] = 169
        lookupTable[1] = 0
        lookupTable[2] = 8
    
        # Set the second 3 values in the lookup table to a red value
        # of 123/255 = 0.482, a green value of 158/255 = 0.62, and
        # a blue value of 222/255 = 0.871. This makes the second entry
        # in the lookup table a shade of blue.
        lookupTable[3] = 123
        lookupTable[4] = 158
        lookupTable[5] = 222
    
        # Create the indexed color space with this color lookup table,
        # using the RGB color space as the base color space and a 2 element
        # color lookup table to characterize the indexed color space.
        theIndexedSpace = Quartz.CGColorSpaceCreateIndexed(theBaseRGBSpace, 1, lookupTable)
        if theIndexedSpace is not None:
            Quartz.CGContextSetStrokeColorSpace(context, theIndexedSpace)
            Quartz.CGContextSetFillColorSpace(context, theIndexedSpace)
    
            # Set the stroke color to an opaque blue.
            Quartz.CGContextSetStrokeColor(context, aBlue)
            # Set the fill color to an opaque red.
            Quartz.CGContextSetFillColor(context, opaqueRed)
    
            Quartz.CGContextSetLineWidth(context, 8.0)
            # Draw the first rectangle.
            Quartz.CGContextBeginPath(context)
            Quartz.CGContextAddRect(context, Quartz.CGRectMake(20.0, 20.0, 100.0, 100.0))
            Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
    
            # Continue to use the stroke colorspace already set
            # but change the stroke alpha value to a semitransparent value
            # while leaving the index value unchanged.
            aBlue = list(aBlue)
            aBlue[1] = 0.5
            Quartz.CGContextSetStrokeColor(context, aBlue)
            # Draw another rectangle to the right of the first one.
            Quartz.CGContextBeginPath(context)
            Quartz.CGContextAddRect(context, Quartz.CGRectMake(140.0, 20.0, 100.0, 100.0))
            Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
        else:
            print("Couldn't make the indexed color space!")
    
    
    def drawWithGlobalAlpha(context):
        rect = Quartz.CGRectMake(40.0, 210.0, 100.0, 100.0)
        color = [1.0, 0.0, 0.0, 1.0]  # opaque red
        # Set the fill color space to that returned by getTheCalibratedRGBColorSpace.
        Quartz.CGContextSetFillColorSpace(
            context, Utilities.getTheCalibratedRGBColorSpace()
        )
    
        Quartz.CGContextSetFillColor(context, color)
        for _ in range(2):
            Quartz.CGContextSaveGState(context)
            # Paint the leftmost rect on this row with 100% opaque red.
            Quartz.CGContextFillRect(context, rect)
    
            Quartz.CGContextTranslateCTM(context, rect.size.width + 70.0, 0.0)
            # Set the alpha value of this rgba color to 0.5.
            color[3] = 0.5
            # Use the new color as the fill color in the graphics state.
            Quartz.CGContextSetFillColor(context, color)
            # Paint the center rect on this row with 50% opaque red.
            Quartz.CGContextFillRect(context, rect)
    
            Quartz.CGContextTranslateCTM(context, rect.size.width + 70.0, 0.0)
            # Set the alpha value of this rgba color to 0.25.
            color[3] = 0.25
            # Use the new color as the fill color in the graphics state.
            Quartz.CGContextSetFillColor(context, color)
            # Paint the rightmost rect on this row with 25% opaque red.
            Quartz.CGContextFillRect(context, rect)
            Quartz.CGContextRestoreGState(context)
            # After restoring the graphics state, the fill color is set to
            # that prior to calling CGContextSaveGState, that is, opaque
            # red. The coordinate system is also restored.
    
            # Now set the context global alpha value to 50% opaque.
            Quartz.CGContextSetAlpha(context, 0.5)
            # Translate down for a second row of rectangles.
            Quartz.CGContextTranslateCTM(context, 0.0, -(rect.size.height + 70.0))
            # Reset the alpha value of the color array to fully opaque.
            color[3] = 1.0
    
    
    def drawWithColorBlendMode(context, url):
        # A pleasant green color.
        green = [0.584, 0.871, 0.318, 1.0]
    
        # Create a CGPDFDocument object from the URL.
        pdfDoc = Quartz.CGPDFDocumentCreateWithURL(url)
        if pdfDoc is None:
            print("Couldn't create CGPDFDocument from URL!")
            return
    
        # Obtain the media box for page 1 of the PDF document.
        pdfRect = Quartz.CGPDFDocumentGetMediaBox(pdfDoc, 1)
        # Set the origin of the rectangle to (0,0).
        pdfRect.origin.x = pdfRect.origin.y = 0
    
        # Graphic 1, the left portion of the figure.
        Quartz.CGContextTranslateCTM(context, 20, 10 + Quartz.CGRectGetHeight(pdfRect) / 2)
    
        # Draw the PDF document.
        Quartz.CGContextDrawPDFDocument(context, pdfRect, pdfDoc, 1)
    
        # Set the fill color space to that returned by getTheCalibratedRGBColorSpace.
        Quartz.CGContextSetFillColorSpace(
            context, Utilities.getTheCalibratedRGBColorSpace()
        )
        # Set the fill color to green.
        Quartz.CGContextSetFillColor(context, green)
    
        # Graphic 2, the top-right portion of the figure.
        Quartz.CGContextTranslateCTM(
            context,
            Quartz.CGRectGetWidth(pdfRect) + 10,
            Quartz.CGRectGetHeight(pdfRect) / 2 + 10,
        )
    
        # Draw the PDF document again.
        Quartz.CGContextDrawPDFDocument(context, pdfRect, pdfDoc, 1)
    
        # Make a fill rectangle that is the same size as the PDF document
        # but inset each side by 80 units in x and 20 units in y.
        insetRect = Quartz.CGRectInset(pdfRect, 80, 20)
        # Fill the rectangle with green. Because the fill color is opaque and
        # the blend mode is Normal, this obscures the drawing underneath.
        Quartz.CGContextFillRect(context, insetRect)
    
        # Graphic 3, the bottom-right portion of the figure.
        Quartz.CGContextTranslateCTM(context, 0, -(10 + Quartz.CGRectGetHeight(pdfRect)))
    
        # Draw the PDF document again.
        Quartz.CGContextDrawPDFDocument(context, pdfRect, pdfDoc, 1)
    
        # Set the blend mode to kCGBlendModeColor which will
        # colorize the destination with subsequent drawing.
        Quartz.CGContextSetBlendMode(context, Quartz.kCGBlendModeColor)
        # Draw the rectangle on top of the PDF document. The portion of the
        # background that is covered by the rectangle is colorized
        # with the fill color.
        Quartz.CGContextFillRect(context, insetRect)
    
    
    def createEllipsePath(context, center, ellipseSize):
        Quartz.CGContextSaveGState(context)
        # Translate the coordinate origin to the center point.
        Quartz.CGContextTranslateCTM(context, center.x, center.y)
        # Scale the coordinate system to half the width and height
        # of the ellipse.
        Quartz.CGContextScaleCTM(context, ellipseSize.width / 2, ellipseSize.height / 2)
        Quartz.CGContextBeginPath(context)
        # Add a circular arc to the path, centered at the origin and
        # with a radius of 1.0. This radius, together with the
        # scaling above for the width and height, produces an ellipse
        # of the correct size.
        Quartz.CGContextAddArc(
            context, 0.0, 0.0, 1.0, 0.0, Utilities.DEGREES_TO_RADIANS(360.0), 0.0
        )
        # Close the path so that this path is suitable for stroking,
        # should that be desired.
        Quartz.CGContextClosePath(context)
        Quartz.CGContextRestoreGState(context)
    
    
    _opaqueBrownColor = None
    _opaqueOrangeColor = None
    
    
    def doClippedEllipse(context):
        global _opaqueBrownColor, _opaqueOrangeColor
    
        theCenterPoint = Quartz.CGPoint(120.0, 120.0)
        theEllipseSize = Quartz.CGSize(100.0, 200.0)
        dash = [2.0]
    
        # Initialize the CGColorRefs if necessary.
        if _opaqueBrownColor is None:
            # The initial value of the color array is an
            # opaque brown in an RGB color space.
            color = [0.325, 0.208, 0.157, 1.0]
            theColorSpace = Utilities.getTheCalibratedRGBColorSpace()
            # Create a CGColorRef for opaque brown.
            _opaqueBrownColor = Quartz.CGColorCreate(theColorSpace, color)
            # Make the color array correspond to an opaque orange.
            color = [0.965, 0.584, 0.059, 1.0]
            # Create another CGColorRef for opaque orange.
            _opaqueOrangeColor = Quartz.CGColorCreate(theColorSpace, color)
    
        # Draw two ellipses centered about the same point, one
        # rotated 45 degrees from the other.
        Quartz.CGContextSaveGState(context)
        # Ellipse 1
        createEllipsePath(context, theCenterPoint, theEllipseSize)
        Quartz.CGContextSetFillColorWithColor(context, _opaqueBrownColor)
        Quartz.CGContextFillPath(context)
        # Translate and rotate about the center point of the ellipse.
        Quartz.CGContextTranslateCTM(context, theCenterPoint.x, theCenterPoint.y)
        # Rotate by 45 degrees.
        Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(45))
        # Ellipse 2
        # CGPointZero is a pre-defined Quartz point corresponding to
        # the coordinate (0,0).
        createEllipsePath(context, Quartz.CGPointZero, theEllipseSize)
        Quartz.CGContextSetFillColorWithColor(context, _opaqueOrangeColor)
        Quartz.CGContextFillPath(context)
        Quartz.CGContextRestoreGState(context)
    
        Quartz.CGContextTranslateCTM(context, 170.0, 0.0)
        # Now use the first ellipse as a clipping area prior to
        # painting the second ellipse.
        Quartz.CGContextSaveGState(context)
        # Ellipse 3
        createEllipsePath(context, theCenterPoint, theEllipseSize)
        Quartz.CGContextSetStrokeColorWithColor(context, _opaqueBrownColor)
        Quartz.CGContextSetLineDash(context, 0, dash, 1)
        # Stroke the path with a dash.
        Quartz.CGContextStrokePath(context)
        # Ellipse 4
        createEllipsePath(context, theCenterPoint, theEllipseSize)
        # Clip to the elliptical path.
        Quartz.CGContextClip(context)
        Quartz.CGContextTranslateCTM(context, theCenterPoint.x, theCenterPoint.y)
        # Rotate by 45 degrees.
        Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(45))
        # Ellipse 5
        createEllipsePath(context, Quartz.CGPointZero, theEllipseSize)
        Quartz.CGContextSetFillColorWithColor(context, _opaqueOrangeColor)
        Quartz.CGContextFillPath(context)
        Quartz.CGContextRestoreGState(context)

.. rst-class:: tabbertab

CoordinateSystem.py
...................

.. sourcecode:: python

    import math
    
    import Quartz
    import Utilities
    
    
    def doRotatedEllipses(context):
        totreps = 144
        tint = 1.0
        tintIncrement = 1.0 / totreps
        # Create a new transform consisting of a 45 degrees rotation.
        theTransform = Quartz.CGAffineTransformMakeRotation(math.pi / 4)
        # Apply a scale to the transform just created.
        theTransform = Quartz.CGAffineTransformScale(theTransform, 1, 2)
        # Place the first ellipse at a good location.
        Quartz.CGContextTranslateCTM(context, 100.0, 100.0)
    
        for _ in range(totreps):
            # Make a snapshot the coordinate system.
            Quartz.CGContextSaveGState(context)
            # Set up the coordinate system for the rotated ellipse.
            Quartz.CGContextConcatCTM(context, theTransform)
            Quartz.CGContextBeginPath(context)
            Quartz.CGContextAddArc(context, 0.0, 0.0, 45.0, 0.0, 2 * math.pi, 0)
            # Set the fill color for this instance of the ellipse.
            Quartz.CGContextSetRGBFillColor(context, tint, 0.0, 0.0, 1.0)
            Quartz.CGContextDrawPath(context, Quartz.kCGPathFill)
            # Restore the coordinate system to that of the snapshot.
            Quartz.CGContextRestoreGState(context)
            # Compute the next tint color.
            tint -= tintIncrement
            # Move over by 1 unit in x for the next ellipse.
            Quartz.CGContextTranslateCTM(context, 1.0, 0.0)
    
    
    def drawSkewedCoordinateSystem(context):
        # alpha is 22.5 degrees and beta is 15 degrees.
        alpha = math.pi / 8
        beta = math.pi / 12
        # Create a rectangle that is 72 units on a side
        # with its origin at (0,0).
        r = Quartz.CGRectMake(0, 0, 72, 72)
    
        Quartz.CGContextTranslateCTM(context, 144, 144)
        # Draw the coordinate axes untransformed.
        Utilities.drawCoordinateAxes(context)
        # Fill the rectangle.
        Quartz.CGContextFillRect(context, r)
    
        # Create an affine transform that skews the coordinate system,
        # skewing the x-axis by alpha radians and the y-axis by beta radians.
        skew = Quartz.CGAffineTransformMake(1, math.tan(alpha), math.tan(beta), 1, 0, 0)
        # Apply that transform to the context coordinate system.
        Quartz.CGContextConcatCTM(context, skew)
    
        # Set the fill and stroke color to a dark blue.
        Quartz.CGContextSetRGBStrokeColor(context, 0.11, 0.208, 0.451, 1)
        Quartz.CGContextSetRGBFillColor(context, 0.11, 0.208, 0.451, 1)
    
        # Draw the coordinate axes again, now transformed.
        Utilities.drawCoordinateAxes(context)
        # Set the fill color again but with a partially transparent alpha.
        Quartz.CGContextSetRGBFillColor(context, 0.11, 0.208, 0.451, 0.7)
        # Fill the rectangle in the transformed coordinate system.
        Quartz.CGContextFillRect(context, r)

.. rst-class:: tabbertab

DataProvidersAndConsumers.py
............................

.. sourcecode:: python

    import os
    import sys
    
    import Cocoa
    import Quartz
    
    
    def createDataProviderFromPathName(path):
        # Create a CFURL for the supplied file system path.
        url = Cocoa.CFURLCreateWithFileSystemPath(
            None, path, Cocoa.kCFURLPOSIXPathStyle, False
        )
        if url is None:
            print("Couldn't create url!")
            return None
    
        # Create a Quartz data provider for the URL.
        dataProvider = Quartz.CGDataProviderCreateWithURL(url)
        if dataProvider is None:
            print("Couldn't create data provider!")
            return None
    
        return dataProvider
    
    
    def createRGBRampDataProvider():
        width = 256
        height = 256
        imageDataSize = width * height * 3
    
        dataP = bytearray(imageDataSize)
    
        #    Build an image that is RGB 24 bits per sample. This is a ramp
        #    where the red component value increases in red from left to
        #    right and the green component increases from top to bottom.
        #
        idx = 0
        for g in range(height):
            for r in range(width):
                dataP[idx] = chr(r) if sys.version_info[0] == 2 else r
                dataP[idx + 1] = chr(g) if sys.version_info[0] == 2 else g
                dataP[idx + 2] = "\0" if sys.version_info[0] == 2 else 0
                idx += 3
    
        # Once this data provider is created, the data associated
        # with dataP MUST be available until Quartz calls the data
        # releaser function 'rgbReleaseRampData'.
        dataProvider = Quartz.CGDataProviderCreateWithData(None, dataP, imageDataSize, None)
        return dataProvider
    
    
    class MyImageDataInfo:
        fp = None
        totalBytesRead = 0
        skippedBytes = 0
        numRewinds = 0
    
    
    def getBytesSequentialAccessDP(data, buffer, count):
        buf = data.fp.read(count)
        buffer[: len(buf)] = buf
        data.totalBytesRead += len(buf)
        return len(buf), buffer
    
    
    def skipBytesSequentialAccessDP(data, count):
        try:
            data.fp.seek(count, os.SEEK_CUR)
            data.skippedBytes += count
    
        except OSError as msg:
            print("Couldn't seek %d bytes because of %s" % (count, msg))
    
    
    def rewindSequentialAccessDP(data):
        # Rewind the beginning of the data.
        data.fp.seek(0, 0)
        data.numRewinds += 1
    
    
    def releaseSequentialAccessDP(data):
        if data is not None:
            print(
                "read %d bytes, skipped %d bytes, rewind called %d times"
                % (data.totalBytesRead, data.skippedBytes, data.numRewinds)
            )
            data.fp.close()
    
    
    def createSequentialAccessDPForURL(url):
        success, pathString = Cocoa.CFURLGetFileSystemRepresentation(url, True, None, 1024)
        pathString = pathString.rstrip(b"\0")
        if not success:
            print("Couldn't get the path name C string!")
            return None
    
        fp = open(pathString, "rb")
        if fp is None:
            print(f"Couldn't open path to file {pathString}!")
            return None
    
        imageDataInfoP = MyImageDataInfo()
        imageDataInfoP.fp = fp
    
        provider = Quartz.CGDataProviderCreate(
            imageDataInfoP,
            (
                getBytesSequentialAccessDP,
                skipBytesSequentialAccessDP,
                rewindSequentialAccessDP,
                releaseSequentialAccessDP,
            ),
        )
        if provider is None:
            print("Couldn't create data provider!")
            # Release the info data and cleanup.
            releaseSequentialAccessDP(imageDataInfoP)
            return None
    
        return provider
    
    
    def getBytesGrayRampDirectAccess(info, buffer, offset, count):
        # This computes a linear gray ramp that is 256 samples wide and
        # 1 sample high. The ith byte in the image is the sample
        # value i. This produces a gray ramp that goes from 0 (black) to
        # FF (white).
        idx = 0
    
        # This data provider provides 256 bytes total. If Quartz
        # requests more data than is available, only return
        # the available data.
        if (offset + count) > 256:
            count = 256 - offset
    
        for i in range(offset, offset + count):
            buffer[idx] = chr(i) if sys.version_info[0] == 2 else i
            idx += 1
    
        return count, buffer
    
    
    def createGrayRampDirectAccessDP():
        provider = Quartz.CGDataProviderCreateDirectAccess(
            None, 256, (None, None, getBytesGrayRampDirectAccess, None)
        )
        if provider is None:
            print("Couldn't create data provider!")
            return None
    
        return provider
    
    
    # This only builds on Tiger and later.
    def myCGDataProviderCreateWithCFData(data):
        # If the CFData object passed in is None, this code returns
        # a None data provider.
        if data is None:
            return None
    
        # Test to see if the Quartz version is available and if so, use it.
    
        # XXX: force the replacement code to be used
        # if hasattr(Quartz, 'CGDataProviderCreateWithCFData'):
        #    return CGDataProviderCreateWithCFData(data)
    
        dataSize = Cocoa.CFDataGetLength(data)
        provider = Quartz.CGDataProviderCreateWithData(data, data, dataSize, None)
        return provider
    
    
    def createDataConsumerFromPathName(path):
        # Create a CFURL for the supplied file system path.
        url = Cocoa.CFURLCreateWithFileSystemPath(
            None, path, Cocoa.kCFURLPOSIXPathStyle, False
        )
        if url is None:
            print("Couldn't create url!")
            return None
        # Create a Quartz data provider for the URL.
        dataConsumer = Quartz.CGDataConsumerCreateWithURL(url)
        if dataConsumer is None:
            print("Couldn't create data consumer!")
            return None
    
        return dataConsumer
    
    
    def myCFDataConsumerPutBytes(data, buffer, count):
        # Append 'count' bytes from 'buffer' to the CFData
        # object 'data'.
        Cocoa.CFDataAppendBytes(data, buffer, count)
        return count
    
    
    # This only builds on Tiger and later.
    def myCGDataConsumerCreateWithCFData(data):
        # If the CFData object passed in is None, this code returns
        # a None data consumer.
        if data is None:
            return None
    
        # Test to see if the Quartz version is available.
    
        # XXX: force the replacement code to be used:
        # if hasattr(Quartz, 'CGDataConsumerCreateWithCFData'):
        #    return CGDataConsumerCreateWithCFData(data)
    
        consumer = Quartz.CGDataConsumerCreate(data, (myCFDataConsumerPutBytes, None))
        return consumer

.. rst-class:: tabbertab

DrawingBasics.py
................

.. sourcecode:: python

    import math
    
    import Quartz
    
    
    def doSimpleRect(context):
        # Set the fill color to opaque red.
        Quartz.CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0)
        # Set up the rectangle for drawing.
        ourRect = Quartz.CGRectMake(20.0, 20.0, 130.0, 100.0)
        # Draw the filled rectangle.
        Quartz.CGContextFillRect(context, ourRect)
    
    
    def doStrokedRect(context):
        # Set the stroke color to a light opaque blue.
        Quartz.CGContextSetRGBStrokeColor(context, 0.482, 0.62, 0.871, 1.0)
        # Set up the rectangle for drawing.
        ourRect = Quartz.CGRectMake(20.0, 20.0, 130.0, 100.0)
        # Draw the stroked rectangle with a line width of 3.
        Quartz.CGContextStrokeRectWithWidth(context, ourRect, 3.0)
    
    
    def doStrokedAndFilledRect(context):
        # Define a rectangle to use for drawing.
        ourRect = Quartz.CGRectMake(20.0, 220.0, 130.0, 100.0)
    
        # ***** Rectangle 1 *****
        # Set the fill color to a light opaque blue.
        Quartz.CGContextSetRGBFillColor(context, 0.482, 0.62, 0.871, 1.0)
        # Set the stroke color to an opaque green.
        Quartz.CGContextSetRGBStrokeColor(context, 0.404, 0.808, 0.239, 1.0)
        # Fill the rect.
        Quartz.CGContextFillRect(context, ourRect)
        # ***** Rectangle 2 *****
        # Move the rectangle's origin to the right by 200 units.
        ourRect.origin.x += 200.0
        # Stroke the rectangle with a line width of 10.
        Quartz.CGContextStrokeRectWithWidth(context, ourRect, 10.0)
        # ***** Rectangle 3 *****
        # Move the rectangle's origin to the left by 200 units
        # and down by 200 units.
        ourRect.origin.x -= 200.0
        ourRect.origin.y -= 200.0
        # Fill then stroke the rect with a line width of 10.
        Quartz.CGContextFillRect(context, ourRect)
        Quartz.CGContextStrokeRectWithWidth(context, ourRect, 10.0)
        # ***** Rectangle 4 *****
        # Move the rectangle's origin to the right by 200 units.
        ourRect.origin.x += 200.0
        # Stroke then fill the rect.
        Quartz.CGContextStrokeRectWithWidth(context, ourRect, 10.0)
        Quartz.CGContextFillRect(context, ourRect)
    
    
    def createRectPath(context, rect):
        # Create a path using the coordinates of the rect passed in.
        Quartz.CGContextBeginPath(context)
        Quartz.CGContextMoveToPoint(context, rect.origin.x, rect.origin.y)
        # ***** Segment 1 *****
        Quartz.CGContextAddLineToPoint(
            context, rect.origin.x + rect.size.width, rect.origin.y
        )
        # ***** Segment 2 *****
        Quartz.CGContextAddLineToPoint(
            context, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height
        )
        # ***** Segment 3 *****
        Quartz.CGContextAddLineToPoint(
            context, rect.origin.x, rect.origin.y + rect.size.height
        )
        # ***** Segment 4 is created by closing the path *****
        Quartz.CGContextClosePath(context)
    
    
    def doPathRects(context):
        # Define a rectangle to use for drawing.
        ourRect = Quartz.CGRectMake(20.0, 20.0, 130.0, 100.0)
    
        # ***** Rectangle 1 *****
        # Create the rect path.
        createRectPath(context, ourRect)
        # Set the fill color to a light opaque blue.
        Quartz.CGContextSetRGBFillColor(context, 0.482, 0.62, 0.871, 1.0)
        # Fill the path.
        Quartz.CGContextDrawPath(context, Quartz.kCGPathFill)  # Clears the path.
        # ***** Rectangle 2 *****
        # Translate the coordinate system 200 units to the right.
        Quartz.CGContextTranslateCTM(context, 200.0, 0.0)
        # Set the stroke color to an opaque green.
        Quartz.CGContextSetRGBStrokeColor(context, 0.404, 0.808, 0.239, 1.0)
        createRectPath(context, ourRect)
        # Set the line width to 10 units.
        Quartz.CGContextSetLineWidth(context, 10.0)
        # Stroke the path.
        Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke)  # Clears the path.
        # ***** Rectangle 3 *****
        # Translate the coordinate system
        # 200 units to the left and 200 units down.
        Quartz.CGContextTranslateCTM(context, -200.0, -200.0)
        createRectPath(context, ourRect)
        # Quartz.CGContextSetLineWidth(context, 10.0)       # This is redundant.
        # Fill, then stroke the path.
        Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)  # Clears the path.
        # ***** Rectangle 4 *****
        # Translate the coordinate system 200 units to the right.
        Quartz.CGContextTranslateCTM(context, 200.0, 0.0)
        createRectPath(context, ourRect)
        # Stroke the path.
        Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke)  # Clears the path.
        # Create the path again.
        createRectPath(context, ourRect)
        # Fill the path.
        Quartz.CGContextDrawPath(context, Quartz.kCGPathFill)  # Clears the path.
    
    
    def doAlphaRects(context):
        # ***** Part 1 *****
        ourRect = Quartz.CGRectMake(0.0, 0.0, 130.0, 100.0)
        numRects = 6
        rotateAngle = 2 * math.pi / numRects
        tintAdjust = 1.0 / numRects
    
        # ***** Part 2 *****
        Quartz.CGContextTranslateCTM(
            context, 2 * ourRect.size.width, 2 * ourRect.size.height
        )
    
        # ***** Part 3 *****
        tint = 1.0
        for _ in range(numRects):
            Quartz.CGContextSetRGBFillColor(context, tint, 0.0, 0.0, tint)
            Quartz.CGContextFillRect(context, ourRect)
            # These transformations are cumulative.
            Quartz.CGContextRotateCTM(context, rotateAngle)
            tint -= tintAdjust
    
    
    def drawStrokedLine(context, start, end):
        Quartz.CGContextBeginPath(context)
        Quartz.CGContextMoveToPoint(context, start.x, start.y)
        Quartz.CGContextAddLineToPoint(context, end.x, end.y)
        Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke)
    
    
    def doDashedLines(context):
        lengths = (12.0, 6.0, 5.0, 6.0, 5.0, 6.0)
    
        start = Quartz.CGPoint(20.0, 270.0)
        end = Quartz.CGPoint(300.0, 270.0)
        # ***** Line 1 solid line *****
        Quartz.CGContextSetLineWidth(context, 5.0)
        drawStrokedLine(context, start, end)
        # ***** Line 2 long dashes *****
        Quartz.CGContextTranslateCTM(context, 0.0, -50.0)
        Quartz.CGContextSetLineDash(context, 0.0, lengths, 2)
        drawStrokedLine(context, start, end)
        # ***** Line 3 long short pattern *****
        Quartz.CGContextTranslateCTM(context, 0.0, -50.0)
        Quartz.CGContextSetLineDash(context, 0.0, lengths, 4)
        drawStrokedLine(context, start, end)
        # ***** Line 4 long short short pattern *****
        Quartz.CGContextTranslateCTM(context, 0.0, -50.0)
        Quartz.CGContextSetLineDash(context, 0.0, lengths, 6)
        drawStrokedLine(context, start, end)
        # ***** Line 5 short short long pattern *****
        Quartz.CGContextTranslateCTM(context, 0.0, -50.0)
        Quartz.CGContextSetLineDash(context, lengths[0] + lengths[1], lengths, 6)
        drawStrokedLine(context, start, end)
        # ***** Line 6 solid line *****
        Quartz.CGContextTranslateCTM(context, 0.0, -50.0)
        # Reset dash to solid line.
        Quartz.CGContextSetLineDash(context, 0, None, 0)
        drawStrokedLine(context, start, end)
    
    
    def doClippedCircle(context):
        circleCenter = Quartz.CGPoint(150.0, 150.0)
        circleRadius = 100.0
        startingAngle = 0.0
        endingAngle = 2 * math.pi
        ourRect = Quartz.CGRectMake(65.0, 65.0, 170.0, 170.0)
    
        # ***** Filled Circle *****
        Quartz.CGContextSetRGBFillColor(context, 0.663, 0.0, 0.031, 1.0)
        Quartz.CGContextBeginPath(context)
        # Construct the circle path counterclockwise.
        Quartz.CGContextAddArc(
            context,
            circleCenter.x,
            circleCenter.y,
            circleRadius,
            startingAngle,
            endingAngle,
            0,
        )
        Quartz.CGContextDrawPath(context, Quartz.kCGPathFill)
    
        # ***** Stroked Square *****
        Quartz.CGContextStrokeRect(context, ourRect)
    
        # Translate so that the next drawing doesn't overlap what
        # has already been drawn.
        Quartz.CGContextTranslateCTM(context, ourRect.size.width + circleRadius + 5.0, 0)
        # Create a rectangular path and clip to that path.
        Quartz.CGContextBeginPath(context)
        Quartz.CGContextAddRect(context, ourRect)
        Quartz.CGContextClip(context)
    
        # ***** Clipped Circle *****
        Quartz.CGContextBeginPath(context)
        # Construct the circle path counterclockwise.
        Quartz.CGContextAddArc(
            context,
            circleCenter.x,
            circleCenter.y,
            circleRadius,
            startingAngle,
            endingAngle,
            0,
        )
        Quartz.CGContextDrawPath(context, Quartz.kCGPathFill)
    
    
    def doPDFDocument(context, url):
        pdfDoc = Quartz.CGPDFDocumentCreateWithURL(url)
        if pdfDoc is not None:
            Quartz.CGContextScaleCTM(context, 0.5, 0.5)
            # The media box is the bounding box of the PDF document.
            pdfRect = Quartz.CGPDFDocumentGetMediaBox(pdfDoc, 1)  # page 1
            # Set the destination rect origin to the Quartz origin.
            pdfRect.origin.x = pdfRect.origin.y = 0.0
            # Draw page 1 of the PDF document.
            Quartz.CGContextDrawPDFDocument(context, pdfRect, pdfDoc, 1)
    
            Quartz.CGContextTranslateCTM(context, pdfRect.size.width * 1.2, 0)
            # Scale non-uniformly making the y coordinate scale 1.5 times
            # the x coordinate scale.
            Quartz.CGContextScaleCTM(context, 1, 1.5)
            Quartz.CGContextDrawPDFDocument(context, pdfRect, pdfDoc, 1)
    
            Quartz.CGContextTranslateCTM(
                context, pdfRect.size.width * 1.2, pdfRect.size.height
            )
            # Flip the y coordinate axis horizontally about the x axis.
            Quartz.CGContextScaleCTM(context, 1, -1)
            Quartz.CGContextDrawPDFDocument(context, pdfRect, pdfDoc, 1)
    
        else:
            print("Can't create PDF document for URL!")

.. rst-class:: tabbertab

EPSPrinting.py
..............

.. sourcecode:: python

    import BitmapContext
    import Cocoa
    import objc
    import Quartz
    import Utilities
    
    # We're using a function that isn't made available through a wrapper, just
    # load it manually:
    if not hasattr(Quartz, "PMCGImageCreateWithEPSDataProvider"):
        functions = [("PMCGImageCreateWithEPSDataProvider", b"@@@")]
        import AppKit
    
        d = {}
        objc.loadBundleFunctions(AppKit.__bundle__, d, functions)
    
        if "PMCGImageCreateWithEPSDataProvider" in d:
            PMCGImageCreateWithEPSDataProvider = d["PMCGImageCreateWithEPSDataProvider"]
        else:
            print("PMCGImageCreateWithEPSDataProvider doesn't exist")
    
    
    def getEPSBBox(epspath):
        try:
            fp = open(epspath)
        except OSError:
            return Quartz.CGRectZero
    
        try:
            #  This is a VERY poor man's EPS DSC parser, here just so that
            #  this sample code can handle simple EPS files. It is
            #  simple but very inefficient. In addition it does not ensure
            #  that the DSC comments are at the beginning of a line,
            #  nor does it handle (atend) style comments at all.
            #  It will simply find the first occurrence of a
            #  %%BoundingBox comment and if it is of the typical
            # form, it will obtain the bounding box data.
            #
            for ln in fp:
                if ln.startswith("%%BoundingBox:"):
                    fields = ln.split()[1:]
                    if len(fields) >= 4:
                        llx = int(fields[0])
                        lly = int(fields[1])
                        urx = int(fields[2])
                        ury = int(fields[3])
                        return Quartz.CGRectMake(llx, lly, urx - llx, ury - lly)
        finally:
            fp.close()
    
        return Quartz.CGRectZero
    
    
    def createEPSPreviewImage(url):
        # The CGImage used as the preview needs to have the
        # same width and height as the EPS data it will
        # be associated with. This sample code doesn't attempt
        # to use any preview image associated with the EPS
        # data but instead simply draws a box of an appropriate
        # size. Your code would most likely create an image
        # that reflects a PICT or TIFF preview present in the
        # EPS data.
        result, path = Cocoa.CFURLGetFileSystemRepresentation(url, True, None, 1024)
        if not result:
            print("Couldn't get the path for EPS file!")
            return None
    
        path = path.rstrip(b"\0")
    
        epsRect = getEPSBBox(path)
        # Check whether the EPS bounding box is empty.
        if epsRect == Quartz.CGRectZero:
            print("Couldn't find BoundingBox comment!")
            return None
    
        wantDisplayColorSpace = False
        needsTransparentBitmap = True
        # Create a bitmap context to draw to in order to
        # create the preview image. Use the routine
        # createRGBBitmapContext from the earlier chapter.
        bitmapContext = BitmapContext.createRGBBitmapContext(
            epsRect.size.width,
            epsRect.size.height,
            wantDisplayColorSpace,
            needsTransparentBitmap,
        )
        if bitmapContext is None:
            print("Couldn't create bitmap context")
            return None
    
        epsRect.origin.x = epsRect.origin.y = 0
        # Draw the contents of the preview. The preview consists
        # of two lines and a stroke around the bounding box. One
        # of the two lines is drawn from the lower-left corner to
        # the upper-right corner of the bounding box and the other
        # line is from the lower-right corner to the upper-left
        # corner of the bounding box.
        Quartz.CGContextBeginPath(bitmapContext)
        Quartz.CGContextMoveToPoint(bitmapContext, 0, 0)
        Quartz.CGContextAddLineToPoint(
            bitmapContext, epsRect.size.width, epsRect.size.height
        )
        Quartz.CGContextMoveToPoint(bitmapContext, epsRect.size.width, 0)
        Quartz.CGContextAddLineToPoint(bitmapContext, 0, epsRect.size.height)
        Quartz.CGContextStrokePath(bitmapContext)
        # Stroke the bounding rectangle, inset so that the stroke is
        # completely contained in the EPS bounding rect.
        Quartz.CGContextStrokeRect(bitmapContext, Quartz.CGRectInset(epsRect, 0.5, 0.5))
    
        # Now create an image from the bitmap raster data. This image
        # has a data provider that releases the image raster data when
        # the image is released. Use the createImageFromBitmapContext
        # from Chapter 12. Calling createImageFromBitmapContext
        # gives up ownership of the raster data used by the context.
        epsPreviewImage = BitmapContext.createImageFromBitmapContext(bitmapContext)
    
        if epsPreviewImage is None:
            print("Couldn't create preview image!")
            return None
    
        return epsPreviewImage
    
    
    # This technique of handling EPS data is available in
    # macOS v10.1 and later and is one alternative method
    # of supporting EPS data during printing as compared to
    # converting EPS data to PDF data using CGPSConverter which
    # is only available in Panther and later.
    def createCGEPSImage(url):
        previewImage = createEPSPreviewImage(url)
        if previewImage is None:
            print("Couldn't create EPS preview!")
            return None
    
        # It is important that the data provider supplying the
        # EPS data conform to the Quartz guidelines for data providers
        # and is able to provide the data until the data releaser function
        # is called. If you have a custom data provider, you need
        # to follow these guidelines since your data provider
        # is not necessarily called before you release the image
        # that uses the provider.
        epsDataProvider = Quartz.CGDataProviderCreateWithURL(url)
        if epsDataProvider is None:
            print("Couldn't create EPS data provider!")
            return None
    
        # Create the hybrid CGImage that contains the preview image
        # and the EPS data. Note that the data provider isn't
        # called during image creation but at some later point in time.
    
        epsImage = PMCGImageCreateWithEPSDataProvider(epsDataProvider, previewImage)
        # The preview image and data provider are no longer needed
        # because Quartz retains them and this code doesn't
        # require them further.
        del previewImage
        del epsDataProvider
    
        if epsImage is None:
            print("Couldn't create EPS hybrid image!")
            return None
    
        return epsImage
    
    
    def drawEPSDataImage(context, url):
        # Create the a CGImage that has EPS data associated with it.
        epsDataImage = createCGEPSImage(url)
        if epsDataImage is None:
            return
    
        # Create a destination rectangle at the location
        # to draw the EPS document. The size of the rect is scaled
        # down to 1/2 the size of the EPS graphic.
        destinationRect = Quartz.CGRectMake(
            100,
            100,
            Quartz.CGImageGetWidth(epsDataImage),
            Quartz.CGImageGetHeight(epsDataImage),
        )
        # Draw the image to the destination. When the EPS
        # data associated with the image is sent to a PostScript
        # printer, the EPS bounding box is mapped to this
        # destination rectangle, translated and scaled as necessary.
        Quartz.CGContextDrawImage(context, destinationRect, epsDataImage)
    
        # Draw the image a second time. This time the image is
        # rotated by 45 degrees and scaled by an additional scaling factor
        # of 0.5 in the x dimension. The center point of this image coincides
        # with the center point of the earlier drawing.
        Quartz.CGContextTranslateCTM(
            context,
            destinationRect.origin.x + destinationRect.size.width / 2,
            destinationRect.origin.y + destinationRect.size.height / 2,
        )
        Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(45))
        Quartz.CGContextScaleCTM(context, 0.5, 1)
        Quartz.CGContextTranslateCTM(
            context,
            -(destinationRect.origin.x + destinationRect.size.width / 2),
            -(destinationRect.origin.y + destinationRect.size.height / 2),
        )
        Quartz.CGContextDrawImage(context, destinationRect, epsDataImage)

.. rst-class:: tabbertab

FrameworkTextDrawing.py
.......................

.. sourcecode:: python

    import Cocoa
    import objc
    import Quartz
    import QuartzTextDrawing
    import Utilities
    from objc import super  # noqa: A004
    
    
    def getTextString():
        # These unicode values are the characters: Q, u, a, r, t, z,
        # eighthnote, floral heart, black chess queen, and two CJK characters.
        # Note: Create an NSString, because we'll use NSString-specific API's, otherwise
        # we could just have used a python unicode object
        return Cocoa.NSString.stringWithString_(
            "\u0051\u0075\u0061\u0072\u0074\u007A\u266A\u2766\u265B\u3042\u304E"
        )
    
    
    doPointDrawing = 1
    
    
    def drawNSStringWithAttributes():
        textString = getTextString()
        if doPointDrawing:
            context = Cocoa.NSGraphicsContext.currentContext().graphicsPort()
    
        # Text Line 1. Draw with default attributes.
        p = Cocoa.NSMakePoint(20.0, 400.0)
    
        # Draw text with default text attributes. The point supplied is
        # not the text baseline but rather the lower-left corner of the box
        # which bounds the text.
        textString.drawAtPoint_withAttributes_(p, None)
    
        if doPointDrawing:
            Utilities.drawPoint(context, p)
    
        # Text Line 2. Draw with a specific font and color.
    
        # Position the text 50 units below the previous text.
        p.y -= 50
    
        # Set attributes to use when drawing the string.
        stringAttributes = {
            # Use the font with the PostScript name "Times-Roman" at 40 point.
            Cocoa.NSFontAttributeName: Cocoa.NSFont.fontWithName_size_("Times-Roman", 40),
            # Set the color attribute to an opaque red.
            Cocoa.NSForegroundColorAttributeName: Cocoa.NSColor.colorWithCalibratedRed_green_blue_alpha_(  # noqa: B950
                0.663, 0, 0.031, 1.0
            ),
        }
    
        # Draw the text.
        textString.drawAtPoint_withAttributes_(p, stringAttributes)
    
        if doPointDrawing:
            Utilities.drawPoint(context, p)
    
        # Text Line 3. Draw stroked text.
    
        # Position the text 50 units below the previous text.
        p.y -= 50
    
        # Panther and later support stroke attributes. A positive value
        # of the stroke width attribute produces text that is stroked rather
        # than filled.
        stringAttributes[Cocoa.NSStrokeWidthAttributeName] = 3.0
        textString.drawAtPoint_withAttributes_(p, stringAttributes)
    
        if doPointDrawing:
            Utilities.drawPoint(context, p)
    
        # Text Line 4. Draw with fill and stroke.
    
        p.y -= 50
    
        # Panther and later support stroke attributes. A negative value
        # of the stroke width attribute results in text that is both filled
        # and stroked.
        stringAttributes[Cocoa.NSStrokeWidthAttributeName] = -3.0
        # Set the stroke color attribute to black.
        stringAttributes[Cocoa.NSStrokeColorAttributeName] = (
            Cocoa.NSColor.colorWithCalibratedRed_green_blue_alpha_(0, 0, 0, 1.0)
        )
    
        textString.drawAtPoint_withAttributes_(p, stringAttributes)
    
        if doPointDrawing:
            Utilities.drawPoint(context, p)
    
        # Text Line 5. Draw at baseline.
        # Tiger and later support the drawWithRect method which allows
        # string text drawing from a point on the text baseline.
        p.y -= 50
        rect = Cocoa.NSRect(origin=p, size=Cocoa.NSSize(0, 0))
        textString.drawWithRect_options_attributes_(
            rect, Cocoa.NSStringDrawingDisableScreenFontSubstitution, stringAttributes
        )
    
        if doPointDrawing:
            Utilities.drawPoint(context, p)
    
    
    _myLayout = None
    _textStorage = None
    _myTextRange = None
    
    
    def drawWithNSLayout():
        global _myLayout, _textStorage, _myTextRange
    
        if _myLayout is None:
            # Initialize the text storage with the string to draw.
            _textStorage = Cocoa.NSTextStorage.alloc().initWithString_(getTextString())
            # Initialize the layout manager to use with the text storage.
            _myLayout = Cocoa.NSLayoutManager.alloc().init()
            # Allocate and initialize a text container object.
            textContainer = Cocoa.NSTextContainer.alloc().init()
            # Add the text container to the layout.
            _myLayout.addTextContainer_(textContainer)
            # Release the text container since the layout retains it and
            # this code no longer needs it.
            del textContainer
            # Add the layout to the text storage.
            _textStorage.addLayoutManager_(_myLayout)
    
            # Set attributes to use when drawing the string.
            stringAttributes = {
                # Use the font with the PostScript name "Times-Roman" at 40 point.
                Cocoa.NSFontAttributeName: Cocoa.NSFont.fontWithName_size_(
                    "Times-Roman", 40
                ),
                # Set the text color attribute to an opaque red.
                Cocoa.NSForegroundColorAttributeName: Cocoa.NSColor.colorWithCalibratedRed_green_blue_alpha_(  # noqa: B950
                    0.663, 0, 0.031, 1.0
                ),
            }
    
            # Create the range of text for the entire length of text
            # in the textStorage object.
            _myTextRange = Cocoa.NSMakeRange(0, _textStorage.length())
            # Set the attributes on the entire range of text.
            _textStorage.setAttributes_range_(stringAttributes, _myTextRange)
    
        # Set the point for drawing the layout.
        p = Cocoa.NSMakePoint(20.0, 400.0)
    
        # Draw the text range at the point.
        _myLayout.drawGlyphsForGlyphRange_atPoint_(_myTextRange, p)
    
        if doPointDrawing:
            context = Cocoa.NSGraphicsContext.currentContext().graphicsPort()
            Utilities.drawPoint(context, p)
    
    
    # The interface to the NSLayoutManager subclass.
    class MyNSLayoutManager(Cocoa.NSLayoutManager):
        # The extra instance variables for this subclass.
        _textMode = objc.ivar()
        _fColor = objc.ivar()
        _sColor = objc.ivar()
        _yStartPosition = objc.ivar()
        _lineWidth = objc.ivar()
        _clippingDrawProc = objc.ivar()
        _clippingInfo = objc.ivar()
    
        # Public methods to set the special attributes
        # of the MyNSLayoutManager instance.
        def setTextMode_(self, textMode):
            self._textMode = textMode
    
        def setFillColor_(self, color):
            self._fColor = color
    
        def setStrokeColor_(self, color):
            self._sColor = color
    
        def setTextLineWidth_(self, width):
            self._lineWidth = width
    
        def setClippingDrawProc_withInfo_(self, clippingDrawProc, info):
            self._clippingDrawProc = clippingDrawProc
            self._clippingInfo = info
    
        def init(self):
            self = super().init()
            if self is None:
                return None
    
            # Initialize the custom instance variables.
            self._textMode = Quartz.kCGTextFill
            self._fColor = None
            self._sColor = None
            self._yStartPosition = 0
            self._lineWidth = 1
            self._clippingDrawProc = None
            self._clippingInfo = None
            return self
    
        # This code overrides this method to record the y coordinate
        # to use as the True baseline for the text drawing.
        def drawGlyphsForGlyphRange_atPoint_(self, glyphsToShow, origin):
            self._yStartPosition = origin.y
            super().drawGlyphsForGlyphRange_atPoint_(glyphsToShow, origin)
    
        # This is the rendering method of NSLayoutManager that the
        # code overrides to perform its custom rendering.
        def showPackedGlyphs_length_glyphRange_atPoint_font_color_printAdjustment_(
            self, glyphs, glyphLen, glyphRange, point, font, color, printingAdjustment
        ):
            # Obtain the destination drawing context.
            context = Cocoa.NSGraphicsContext.currentContext().graphicsPort()
    
            # Adjust start position y value based on the adjusted y coordinate.
            # This ensures the text baseline is at the starting position
            # passed to drawGlyphsForGlyphRange. This technique won't work
            # for super, subscripts, or underlines but that's OK for this example.
            point.y = self._yStartPosition
    
            # The Quartz graphics state should be preserved by showPackedGlyphs.
            Quartz.CGContextSaveGState(context)
    
            # Set the desired text drawing mode.
            Quartz.CGContextSetTextDrawingMode(context, self._textMode)
    
            # Set the fill color if needed.
            if (
                self._textMode == Quartz.kCGTextFill
                or self.textMode == Quartz.kCGTextFillStroke
                or self._textMode == Quartz.kCGTextFillClip
                or self._textMode == Quartz.kCGTextFillStrokeClip
            ):
                if self._fColor is not None:
                    Quartz.CGContextSetFillColorWithColor(context, self._fColor)
    
            # Set the  line width and the stroke color if needed.
            if (
                self._textMode == Quartz.kCGTextStroke
                or self._textMode == Quartz.kCGTextFillStroke
                or self._textMode == Quartz.kCGTextStrokeClip
                or self._textMode == Quartz.kCGTextFillStrokeClip
            ):
                Quartz.CGContextSetLineWidth(context, self._lineWidth)
                if self._sColor is not None:
                    Quartz.CGContextSetStrokeColorWithColor(context, self._sColor)
    
            # Now draw the text. Check whether to adjust for printing widths
            # and if needed adjust extra character spacing accordingly.
            if printingAdjustment.width != 0.0:
                # If printingAdjustment width is non-zero then the text
                # needs to be adjusted. printingAdjustment is the per character
                # adjustment required for this piece of text. Because
                # the Quartz text character spacing set is transformed by
                # the text matrix, this code needs to factor out that effect
                # prior to setting it. Cocoa sets the text matrix to account
                # for the point size of the font so we factor that out of the
                # per character width supplied here.
                charAdjust = printingAdjustment.width / font.pointSize()
                Quartz.CGContextSetCharacterSpacing(context, charAdjust)
            else:
                Quartz.CGContextSetCharacterSpacing(context, 0.0)
    
            # Draw the glyphs. The total number of glyphs is the length
            # of the glyphs string passed to showPackedGlyphs, divided by 2
            # since there are two bytes per glyph.
            Quartz.CGContextShowGlyphsAtPoint(
                context, point.x, point.y, glyphs, glyphLen / 2
            )
    
            # If the text drawing mode requires clipping and there is
            # a custom clipping proc, call it. This allows drawing through
            # clipped text before the graphics state is restored.
            if (
                self._textMode == Quartz.kCGTextClip
                or self._textMode == Quartz.kCGTextFillClip
                or self._textMode == Quartz.kCGTextStrokeClip
                or self._textMode == Quartz.kCGTextFillStrokeClip
            ) and self._clippingDrawProc is not None:
                self._clippingDrawProc(context, point.x, point.y, self._clippingInfo)
    
            Quartz.CGContextRestoreGState(context)
    
    
    def MyClipProc(c, x, y, info):
        Quartz.CGContextTranslateCTM(c, x, y)
        Quartz.CGContextSetStrokeColorWithColor(c, Utilities.getRGBOpaqueBlackColor())
        # Draw a grid of lines through the clip.
        QuartzTextDrawing.drawGridLines(c)
    
    
    _myLayout2 = None
    _textStorage2 = None
    _myTextRange2 = None
    
    
    def drawWithCustomNSLayout():
        global _myLayout2, _textStorage2, _myTextRange2
    
        if _myLayout2 is None:
            textContainer = Cocoa.NSTextContainer.alloc().init()
    
            _textStorage2 = Cocoa.NSTextStorage.alloc().initWithString_(getTextString())
            # Create an instance of the MyNSLayoutManager subclass of NSLayoutManager.
            _myLayout2 = MyNSLayoutManager.alloc().init()
            _myLayout2.addTextContainer_(textContainer)
            # The layout retains the text container so this code can release it.
            del textContainer
            _textStorage2.addLayoutManager_(_myLayout2)
    
            # Set attributes to use when drawing the string.
            stringAttributes = {
                # Use the font with the PostScript name "Times-Roman" at 40 point.
                Cocoa.NSFontAttributeName: Cocoa.NSFont.fontWithName_size_(
                    "Times-Roman", 40
                )
            }
    
            # Create the range.
            _myTextRange2 = Cocoa.NSMakeRange(0, _textStorage2.length())
            # Set the attributes on the entire range of text.
            _textStorage2.setAttributes_range_(stringAttributes, _myTextRange2)
    
        p = Cocoa.NSMakePoint(20.0, 400.0)
    
        # Set the custom attributes of the layout subclass so that
        # the text will be filled with black.
        _myLayout2.setTextMode_(Quartz.kCGTextFill)
        _myLayout2.setFillColor_(Utilities.getRGBOpaqueBlackColor())
    
        # Draw text line 1.
        _myLayout2.drawGlyphsForGlyphRange_atPoint_(_myTextRange2, p)
    
        if doPointDrawing:
            context = Cocoa.NSGraphicsContext.currentContext().graphicsPort()
            Utilities.drawPoint(context, p)
    
        # Set the custom attributes of the layout subclass so that
        # the text will be stroked with black.
        _myLayout2.setTextMode_(Quartz.kCGTextStroke)
        _myLayout2.setStrokeColor_(Utilities.getRGBOpaqueBlackColor())
        _myLayout2.setTextLineWidth_(2)
    
        # Draw text line 2.
        p.y -= 50
        _myLayout2.drawGlyphsForGlyphRange_atPoint_(_myTextRange2, p)
    
        if doPointDrawing:
            Utilities.drawPoint(context, p)
    
        p.y -= 50
    
        # Set the custom attributes of the layout subclass so that
        # the text will be filled and stroked and the fill color
        # will be red. Since the stroke color hasn't changed it
        # will be stroked with black.
        _myLayout2.setTextMode_(Quartz.kCGTextFillStroke)
        _myLayout2.setFillColor_(Utilities.getRGBOpaqueRedColor())
        # Draw text line 3.
        _myLayout2.drawGlyphsForGlyphRange_atPoint_(_myTextRange2, p)
    
        if doPointDrawing:
            Utilities.drawPoint(context, p)
    
        p.y -= 50
    
        # Set the custom attributes of the layout subclass so that
        # the text will be filled, stroked, then clipped.
        _myLayout2.setTextMode_(Quartz.kCGTextFillStrokeClip)
    
        # Set the clipping proc to MyClipProc which requires
        # no info data.
        _myLayout2.setClippingDrawProc_withInfo_(MyClipProc, None)
    
        # Draw text line 4.
        _myLayout2.drawGlyphsForGlyphRange_atPoint_(_myTextRange2, p)
    
        if doPointDrawing:
            Utilities.drawPoint(context, p)
    
        # Set the clipping proc to None for future drawing.
        _myLayout2.setClippingDrawProc_withInfo_(None, None)

.. rst-class:: tabbertab

FrameworkUtilities.py
.....................

.. sourcecode:: python

    import AppDrawing  # noqa: F401
    from PDFHandling import cfDataCreatePDFDocumentFromCommand
    import Cocoa
    
    
    def myCreatePDFDataFromPasteBoard():
        # Obtain the pasteboard to examine.
        pboard = Cocoa.NSPasteboard.generalPasteboard()
    
        # Scan the types of data on the pasteboard and return the first type available that is
        # listed in the array supplied. Here the array of requested types contains only one type,
        # that of PDF data. If your application could handle more types, you would list them in
        # the creation of this array.
        pboard_type = pboard.availableTypeFromArray_([Cocoa.NSPDFPboardType])
    
        # If the string is non-nil, there was data of one of our requested types
        # on the pasteboard that can be obtained.
        if pboard_type is not None:
            # Test that the type is the PDF data type. This code is not strictly necessary
            # for this example since we only said we could handle PDF data, but is appropriate
            # if you can handle more types than just PDF.
            if pboard_type == Cocoa.NSPDFPboardType:
                # Get the PDF data from the pasteboard.
                pdfData = pboard.dataForType_(pboard_type)
                if pdfData is not None:
                    return pdfData
                else:
                    Cocoa.NSLog("Couldn't get PDF data from pasteboard!")
        else:
            Cocoa.NSLog("Pasteboard doesn't contain PDF data!")
        return None
    
    
    def addPDFDataToPasteBoard(command):
        pdfData = cfDataCreatePDFDocumentFromCommand(command)
        if pdfData is not None:
            pboard = Cocoa.NSPasteboard.generalPasteboard()
            pboard.declareTypes_owner_(Cocoa.NSPDFPboardType, None)
            pboard.setData_forType_(pdfData, Cocoa.NSPDFPboardType)

.. rst-class:: tabbertab

ImageMasking.py
...............

.. sourcecode:: python

    import sys
    
    import Cocoa
    
    # import DataProvidersAndConsumers
    import Images
    import Quartz
    import Utilities
    
    
    def exportImageWithMaskFromURLWithDestination(
        context,
        imageURL,
        imagewidth,
        imageheight,
        bitsPerComponent,
        theMaskingImageURL,
        maskwidth,
        maskheight,
    ):
        imageBitsPerPixel = bitsPerComponent * 3
        bytesPerRow = ((imagewidth * imageBitsPerPixel) + 7) / 8
        shouldInterpolate = True
        imageDataProvider = Quartz.CGDataProviderCreateWithURL(imageURL)
        if imageDataProvider is None:
            print("Couldn't create Image Data provider!")
            return
    
        colorspace = Utilities.getTheCalibratedRGBColorSpace()
        image = Quartz.CGImageCreate(
            imagewidth,
            imageheight,
            bitsPerComponent,
            imageBitsPerPixel,
            bytesPerRow,
            colorspace,
            Quartz.kCGImageAlphaNone,
            imageDataProvider,
            None,
            shouldInterpolate,
            Quartz.kCGRenderingIntentDefault,
        )
        del imageDataProvider
        if image is None:
            print("Couldn't create CGImageRef for this data!")
            return
    
        imageRect = Quartz.CGRectMake(0.0, imageheight, imagewidth, imageheight)
        # Draw the image.
        Quartz.CGContextDrawImage(context, imageRect, image)
    
        # Now the mask.
        maskDataProvider = Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)
        if maskDataProvider is None:
            print("Couldn't create Image Data provider!")
            return
    
        mask = Quartz.CGImageMaskCreate(
            maskwidth,
            maskheight,
            bitsPerComponent,
            bitsPerComponent,
            maskwidth,
            maskDataProvider,
            None,
            shouldInterpolate,
        )
        del maskDataProvider
        if mask is None:
            print("Couldn't create CGImageRef for mask data!")
            return
    
        # Draw the mask below the image.
        maskRect = Quartz.CGRectMake(0.0, 0.0, maskwidth, maskheight)
        Quartz.CGContextDrawImage(context, maskRect, mask)
    
        # Create a new CGImage object, the image, masked with mask.
        imageMaskedWithImage = Quartz.CGImageCreateWithMask(image, mask)
        # Once the new image is created, we can release the image
        # and the mask which make it up. Quartz retains what it needs
        # for the new masked image.
        del image
        del mask
    
        if imageMaskedWithImage is None:
            print("Couldn't create image masked with mask!")
            return
    
        imageRect = Quartz.CGRectMake(imagewidth, imageheight / 2, imagewidth, imageheight)
        # Draw the masked image to the right of the image and its mask.
        Quartz.CGContextDrawImage(context, imageRect, imageMaskedWithImage)
    
        # Of course this is a total hack.
        outPath = b"/tmp/imageout.png"
        exportURL = Cocoa.CFURLCreateFromFileSystemRepresentation(
            None, outPath, len(outPath), False
        )
    
        if exportURL is not None:
            Images.exportCGImageToPNGFileWithDestination(imageMaskedWithImage, exportURL)
    
    
    if sys.version_info[0] == 2:
    
        def make_bytes(values):
            return "".join(map(chr, values))
    
    else:
    
        def make_bytes(values):
            return bytes(values)
    
    
    _data = make_bytes(
        (
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x7F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x1F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF8,
            0x00,
            0x03,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x00,
            0xF8,
            0xE7,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x00,
            0x00,
            0x40,
            0x7F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xC0,
            0x00,
            0x00,
            0x00,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFC,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x03,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF8,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x01,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF8,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x01,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x3F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xC0,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x3F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x80,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x0F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x0F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x07,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x06,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x00,
            0x00,
            0x01,
            0xC0,
            0x00,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x00,
            0x00,
            0x0F,
            0xF8,
            0x00,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x00,
            0x0F,
            0xFF,
            0xF8,
            0x00,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x00,
            0x1F,
            0xFF,
            0xFC,
            0x00,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x00,
            0x7F,
            0xFF,
            0xFC,
            0x00,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xF8,
            0x00,
            0x00,
            0x00,
            0xFF,
            0xFF,
            0xFC,
            0x00,
            0x00,
            0xFF,
            0xFF,
            0xFF,
            0xF8,
            0x00,
            0x00,
            0x03,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x00,
            0x03,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xF8,
            0x00,
            0x00,
            0x0F,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xF8,
            0x00,
            0x00,
            0x0F,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0xFF,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x00,
            0x3F,
            0xFF,
            0xFF,
            0xFF,
            0x80,
            0x01,
            0xFF,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xFF,
            0x80,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xFF,
            0x80,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x00,
            0x3F,
            0xFF,
            0xFF,
            0xFF,
            0xC0,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xF8,
            0x00,
            0x00,
            0x3F,
            0xFF,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xF8,
            0x00,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xFF,
            0xC0,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xFC,
            0x00,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xFE,
            0x40,
            0x00,
            0x0F,
            0xFF,
            0xFF,
            0xFC,
            0x00,
            0x00,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xFC,
            0x00,
            0x00,
            0xFF,
            0xFF,
            0xFF,
            0xC0,
            0x00,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xFC,
            0x00,
            0x01,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0x00,
            0x3F,
            0xFF,
            0xFF,
            0xFC,
            0x00,
            0x05,
            0xEF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x00,
            0x3F,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x3F,
            0x00,
            0x03,
            0xFC,
            0x00,
            0x00,
            0x00,
            0x3F,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x7C,
            0x00,
            0x00,
            0x78,
            0x1F,
            0x00,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xC0,
            0x00,
            0x38,
            0x00,
            0x00,
            0x78,
            0x3C,
            0x00,
            0x01,
            0xFF,
            0xFF,
            0xFF,
            0xC0,
            0x00,
            0x78,
            0x00,
            0x00,
            0x70,
            0x18,
            0x00,
            0x01,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x78,
            0x1F,
            0x00,
            0x30,
            0x00,
            0x00,
            0x01,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x7C,
            0x3F,
            0x00,
            0x18,
            0x00,
            0x00,
            0x01,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x00,
            0x00,
            0x38,
            0x00,
            0x00,
            0x03,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x80,
            0x00,
            0x3C,
            0x00,
            0x0C,
            0x03,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0x00,
            0x00,
            0x3C,
            0x20,
            0x1C,
            0x03,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0x04,
            0x00,
            0x3C,
            0x00,
            0x3C,
            0x03,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x70,
            0xBF,
            0x86,
            0x3C,
            0x1F,
            0xFC,
            0x0B,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xA0,
            0x11,
            0xF0,
            0x0E,
            0x3C,
            0x1F,
            0xFE,
            0x8B,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xA0,
            0x19,
            0xF0,
            0x0C,
            0x3C,
            0x0F,
            0xFF,
            0x0B,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xB0,
            0x1D,
            0xFE,
            0x1C,
            0x7E,
            0x0F,
            0xFF,
            0x03,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xB8,
            0x1C,
            0xFF,
            0x3C,
            0xFE,
            0x03,
            0xFE,
            0x03,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFC,
            0x1E,
            0x7F,
            0xF8,
            0xDE,
            0x00,
            0x7C,
            0x03,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x1E,
            0x7F,
            0xF1,
            0xDF,
            0x30,
            0x03,
            0x83,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x1F,
            0x3F,
            0xE3,
            0x9F,
            0x10,
            0x3F,
            0x83,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x0F,
            0xFF,
            0x83,
            0xDF,
            0x80,
            0x1F,
            0x83,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x03,
            0xFC,
            0x03,
            0xDF,
            0x81,
            0x8F,
            0x83,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x07,
            0xFE,
            0x1F,
            0x8F,
            0x00,
            0x07,
            0x83,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x07,
            0xFE,
            0x3C,
            0x06,
            0x00,
            0x01,
            0x83,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x03,
            0xFC,
            0x7C,
            0x00,
            0x00,
            0x01,
            0x83,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x01,
            0xF8,
            0x7F,
            0x00,
            0x00,
            0x01,
            0x83,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x01,
            0xF8,
            0xFF,
            0xE0,
            0x30,
            0x01,
            0x83,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0xF1,
            0xEF,
            0xF9,
            0xE0,
            0x03,
            0x83,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x80,
            0xF1,
            0xFF,
            0xFF,
            0x80,
            0x0F,
            0x83,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFC,
            0x03,
            0xE2,
            0xFF,
            0xFE,
            0x00,
            0x1F,
            0x87,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x83,
            0xF0,
            0x00,
            0x00,
            0x1C,
            0x3F,
            0x87,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xC3,
            0xF0,
            0x00,
            0x01,
            0xF8,
            0x0F,
            0x87,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xC3,
            0xF0,
            0x03,
            0xFF,
            0xF0,
            0x5F,
            0x07,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xC1,
            0xFF,
            0xC7,
            0xFF,
            0xE0,
            0x7F,
            0x0F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xE1,
            0xFF,
            0xF1,
            0xFF,
            0x80,
            0x2F,
            0x0F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xE1,
            0xFF,
            0xF8,
            0x0F,
            0xC0,
            0x06,
            0x1F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF4,
            0xFF,
            0xFE,
            0x0F,
            0xF8,
            0x44,
            0x1F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF4,
            0xFF,
            0xFF,
            0xFF,
            0xF8,
            0x64,
            0x3F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF9,
            0xFF,
            0xFF,
            0xFF,
            0x3C,
            0xE4,
            0x7F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFD,
            0x9F,
            0xFF,
            0xFC,
            0x1F,
            0xC0,
            0x7F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFD,
            0x1F,
            0xFF,
            0xFC,
            0x03,
            0xC0,
            0x7F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x01,
            0xFF,
            0xFF,
            0xFF,
            0xC0,
            0x7F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x80,
            0x7F,
            0xFF,
            0xFF,
            0x00,
            0x3F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x80,
            0x1F,
            0xFF,
            0xFF,
            0x00,
            0x3F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0xC0,
            0x0F,
            0xFF,
            0xFF,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xE0,
            0x07,
            0xFF,
            0xFF,
            0x00,
            0x0F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x03,
            0xFF,
            0xFF,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x70,
            0x01,
            0xFF,
            0xFC,
            0x00,
            0x17,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFC,
            0x78,
            0x00,
            0x7F,
            0xF0,
            0x00,
            0x07,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0xFC,
            0x00,
            0x00,
            0x00,
            0x00,
            0x07,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x03,
            0xFE,
            0x00,
            0x00,
            0x00,
            0x00,
            0x07,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x1F,
            0xFF,
            0x80,
            0x00,
            0x00,
            0x00,
            0x07,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFC,
            0x7F,
            0x7F,
            0xC0,
            0x00,
            0x00,
            0x00,
            0x07,
            0xFF,
            0xFF,
        )
    )
    
    
    def getMaskData1():
        return _data
    
    
    _data2 = make_bytes(
        (
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x03,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFC,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x01,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x80,
            0x00,
            0x00,
            0x3F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFC,
            0x00,
            0x00,
            0x00,
            0x07,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF8,
            0x00,
            0x00,
            0x00,
            0x03,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x00,
            0x00,
            0x01,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x00,
            0x00,
            0x01,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xC0,
            0x00,
            0x00,
            0x00,
            0x00,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x80,
            0x00,
            0x00,
            0x00,
            0x00,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x3F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x3F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFC,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF8,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF8,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x0F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x00,
            0x00,
            0x01,
            0x80,
            0x07,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x00,
            0x00,
            0x03,
            0xC0,
            0x07,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x00,
            0x00,
            0x0B,
            0xE0,
            0x07,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x00,
            0x00,
            0x07,
            0xF0,
            0x03,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x00,
            0x00,
            0x1F,
            0xF4,
            0x83,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xC0,
            0x00,
            0x00,
            0x00,
            0x3F,
            0xE4,
            0x03,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xC0,
            0x00,
            0x00,
            0x00,
            0x3F,
            0xE4,
            0x43,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x80,
            0x00,
            0x00,
            0x00,
            0x3F,
            0xE4,
            0x4B,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x80,
            0x00,
            0x00,
            0x02,
            0xFF,
            0xE4,
            0x5B,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0x00,
            0x02,
            0xFF,
            0xE0,
            0x5B,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0x07,
            0xC1,
            0xFF,
            0xE0,
            0x59,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x18,
            0x00,
            0x7F,
            0xF0,
            0xFE,
            0x00,
            0x79,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x18,
            0x00,
            0x78,
            0x0F,
            0xFE,
            0x04,
            0xE1,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x18,
            0x00,
            0xB0,
            0x47,
            0xFF,
            0xFF,
            0xE1,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x10,
            0x00,
            0xC4,
            0x69,
            0xFF,
            0xFF,
            0xC3,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFE,
            0x10,
            0x01,
            0xFF,
            0xE1,
            0xFC,
            0x07,
            0xC1,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFC,
            0x00,
            0x01,
            0xFF,
            0xF8,
            0x78,
            0x01,
            0xC5,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFC,
            0x04,
            0x01,
            0xFF,
            0xF0,
            0x78,
            0x01,
            0xC5,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFC,
            0x0C,
            0x00,
            0xFF,
            0xF8,
            0x7E,
            0x3F,
            0xC1,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFC,
            0x0C,
            0x00,
            0x7F,
            0xF0,
            0x18,
            0xFF,
            0xC3,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFC,
            0x07,
            0x00,
            0x7F,
            0xF4,
            0x1F,
            0xFF,
            0xE3,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFC,
            0x23,
            0x00,
            0x7F,
            0xE0,
            0x3E,
            0xFF,
            0xE3,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF8,
            0x11,
            0x00,
            0x7F,
            0xEC,
            0x5F,
            0xBF,
            0xE3,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF8,
            0x01,
            0x00,
            0x3F,
            0xCE,
            0x7E,
            0x3F,
            0xE3,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF8,
            0x08,
            0x00,
            0x7F,
            0x80,
            0x2E,
            0x3F,
            0xE3,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF8,
            0x06,
            0x00,
            0x7F,
            0x00,
            0x6E,
            0x3F,
            0xEB,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x00,
            0x7E,
            0x0D,
            0xFE,
            0xFF,
            0xEB,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x00,
            0x3C,
            0x00,
            0xFE,
            0x3F,
            0xEB,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x00,
            0x50,
            0x00,
            0xFE,
            0x3F,
            0xCB,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x00,
            0x01,
            0x98,
            0xFF,
            0x3F,
            0xCB,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x00,
            0xC7,
            0xE1,
            0xFF,
            0x3F,
            0x8B,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x00,
            0x40,
            0xFF,
            0xFF,
            0xBF,
            0x8B,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x00,
            0xE0,
            0x1F,
            0xFF,
            0xDF,
            0x0B,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x00,
            0xE8,
            0x63,
            0xFF,
            0xDF,
            0x0F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x02,
            0xFC,
            0xF9,
            0xFF,
            0xEF,
            0x0F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x03,
            0xFE,
            0x7F,
            0xF8,
            0x1E,
            0x0F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x03,
            0x7E,
            0x0F,
            0xF9,
            0xBE,
            0x05,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x01,
            0x7F,
            0xC1,
            0xF3,
            0xFC,
            0x05,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x01,
            0x3D,
            0xF8,
            0x0F,
            0x7C,
            0x05,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x01,
            0xBC,
            0x7F,
            0xFF,
            0xF8,
            0x02,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0x00,
            0xBE,
            0xFF,
            0xFF,
            0xF8,
            0x02,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xC0,
            0x00,
            0x00,
            0x1D,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xC0,
            0x00,
            0x00,
            0x0F,
            0xF7,
            0xFF,
            0xE0,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xFF,
            0xC0,
            0x00,
            0x00,
            0x0F,
            0xF3,
            0xFF,
            0xE0,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xFF,
            0x80,
            0x00,
            0x00,
            0x03,
            0xF3,
            0xFF,
            0xC0,
            0x00,
            0x7F,
            0xFF,
            0xFF,
            0xFF,
            0x80,
            0x00,
            0x00,
            0x01,
            0xF7,
            0xFF,
            0x80,
            0x00,
            0x3F,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0x00,
            0x00,
            0x3F,
            0xFF,
            0x00,
            0x00,
            0x3F,
            0xFF,
            0xFF,
            0xFF,
            0x00,
            0x00,
            0x00,
            0x00,
            0x1F,
            0xFF,
            0x00,
            0x20,
            0x3F,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x00,
            0x00,
            0x1F,
            0xFF,
            0x00,
            0x10,
            0x3F,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x00,
            0x00,
            0x1F,
            0xFF,
            0x00,
            0x10,
            0x3F,
            0xFF,
            0xFF,
            0xFC,
            0x00,
            0x00,
            0x02,
            0x00,
            0x0F,
            0xFF,
            0x00,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xFC,
            0x00,
            0x00,
            0x04,
            0x00,
            0x1F,
            0xFE,
            0x00,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xF8,
            0x00,
            0x00,
            0x07,
            0x81,
            0x7F,
            0xFE,
            0x00,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xF8,
            0x00,
            0x00,
            0x03,
            0xDF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xF8,
            0x00,
            0x00,
            0x07,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x00,
            0x07,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x1F,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0x40,
            0x07,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x0F,
            0xFF,
            0xFF,
            0xF0,
            0x00,
            0xC0,
            0x03,
            0xFF,
            0xFF,
            0xFE,
            0x00,
            0x00,
            0x0F,
            0xFF,
            0xFF,
            0xE0,
            0x00,
            0xE0,
            0x07,
            0xFF,
            0xFF,
            0xFE,
            0x81,
            0x00,
            0x07,
            0xFF,
            0xFF,
            0xC0,
            0x01,
            0xE0,
            0x07,
            0xFF,
            0xFF,
            0xFE,
            0x01,
            0x00,
            0x07,
            0xFF,
            0xFF,
            0x80,
            0x0F,
            0xF0,
            0x03,
            0xFF,
            0xFF,
            0xFE,
            0x83,
            0x80,
            0x03,
            0xFF,
            0xFF,
            0x00,
            0x1F,
            0xF0,
            0x13,
            0xFF,
            0xFF,
            0xFE,
            0x03,
            0xE0,
            0x01,
            0xFF,
            0xFC,
            0x03,
            0x3F,
            0xF0,
            0x21,
            0xFF,
            0xFF,
            0xFE,
            0x03,
            0xFC,
            0x00,
            0x3F,
            0xF0,
            0x3F,
            0x3F,
            0xF8,
            0x3B,
            0xFF,
            0xFF,
            0xFE,
            0x03,
            0xFE,
            0xC0,
            0x0F,
            0xE3,
            0xFB,
            0x7F,
            0xF8,
            0x3B,
            0xFF,
            0xFF,
            0xFF,
            0x07,
            0xFF,
            0xFF,
            0x07,
            0x9F,
            0xFB,
            0x7F,
            0xFC,
            0x79,
            0xFF,
            0xFF,
            0xFF,
            0x07,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x7F,
            0xFC,
            0x39,
            0xFF,
            0xFF,
            0xFF,
            0x07,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x7F,
            0xFE,
            0x3F,
            0xFF,
            0xFF,
            0xFE,
            0x07,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x7F,
            0xFE,
            0x1F,
            0xFF,
            0xFF,
            0xFE,
            0x0F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x7F,
            0xFE,
            0x1F,
            0x7F,
            0xFF,
            0xFE,
            0x0F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x7F,
            0xFF,
            0x1F,
            0xFE,
            0xFF,
            0xFC,
            0x0F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x0F,
            0xFF,
            0xFF,
            0xFF,
            0x1F,
            0xFF,
            0xEF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0x87,
            0xFF,
            0xFF,
            0xFE,
            0x1F,
            0xFF,
            0xEF,
            0xFF,
            0xFF,
            0xFF,
            0xBF,
            0xFF,
            0x82,
            0xFF,
            0xFF,
            0xFC,
            0x3F,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xBF,
            0xFF,
            0x83,
            0xFF,
            0xFF,
            0xFC,
            0x3F,
            0xFF,
            0xBF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xC1,
            0xFF,
            0xFF,
            0xF8,
            0x7F,
            0xFF,
            0xBF,
            0xFF,
            0xFF,
            0xFF,
            0xBF,
            0xFF,
            0xE0,
            0xFF,
            0xFF,
            0xF0,
            0xFF,
            0xFF,
            0xBF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            0xFF,
        )
    )
    
    
    def getMaskData2():
        return _data2
    
    
    def doOneBitMaskImages(context):
        bitsPerComponent = 1
        bitsPerPixel = 1
        width = 96
        height = 96
        bytesPerRow = 12
    
        imageDataSize = bytesPerRow * height
        shouldInterpolate = True
        lightBlue = [0.482, 0.62, 0.871, 1.0]
        black = [0.0, 0.0, 0.0, 1.0]
        darkRed = [0.663, 0.0, 0.031, 1.0]
        darkGreen = [0.404, 0.808, 0.239, 1.0]
        darkBlue = [0.11, 0.208, 0.451, 1.0]
        purple = [0.69, 0.486, 0.722, 1.0]
        darkOrange = [0.965, 0.584, 0.059, 1.0]
    
        # A decode array contains two elements for each component. In this
        # case, an image mask has one component so the array consists of
        # two values. When using this decode array, a sample value of 0
        # is mapped into the value 1, and the maximum sample value is
        # mapped into the value 0. This inverts the sense of the mask data.
        decode = (1, 0)
    
        # Create a Quartz data provider for the image data. Because this
        # data is static data, we don't need to release it so the data
        # release function is None.
        data = getMaskData1()
        dataProvider = Quartz.CGDataProviderCreateWithData(None, data, imageDataSize, None)
        if dataProvider is None:
            print("Couldn't create Mask1 Data provider!")
            return
    
        # Create a mask from the data.
        mask1 = Quartz.CGImageMaskCreate(
            width,
            height,
            bitsPerComponent,
            bitsPerPixel,
            bytesPerRow,
            dataProvider,
            None,
            shouldInterpolate,
        )
        # Create the same mask but with a decode array that
        # inverts the sense of the mask.
        invertedmask1 = Quartz.CGImageMaskCreate(
            width,
            height,
            bitsPerComponent,
            bitsPerPixel,
            bytesPerRow,
            dataProvider,
            decode,
            shouldInterpolate,
        )
        # Release the data provider now that this code no longer needs it.
        del dataProvider
    
        if mask1 is None or invertedmask1 is None:
            if mask1 is None:
                print("Couldn't create CGImageRef for the mask data 1!")
    
            if invertedmask1 is None:
                print("Couldn't create CGImageRef for the inverted mask data 1!")
            return
    
        # Get the pointer to the data for the second mask.
        data = getMaskData2()
        dataProvider = Quartz.CGDataProviderCreateWithData(None, data, imageDataSize, None)
        if dataProvider is None:
            print("Couldn't create Mask2 Data provider!")
            return
    
        mask2 = Quartz.CGImageMaskCreate(
            width,
            height,
            bitsPerComponent,
            bitsPerPixel,
            bytesPerRow,
            dataProvider,
            None,
            shouldInterpolate,
        )
        # Create the same mask but with a decode array that
        # inverts the sense of the mask.
        invertedmask2 = Quartz.CGImageMaskCreate(
            width,
            height,
            bitsPerComponent,
            bitsPerPixel,
            bytesPerRow,
            dataProvider,
            decode,
            shouldInterpolate,
        )
        # Release the data provider now that this code no longer needs it.
        del dataProvider
        if mask2 is None or invertedmask2 is None:
            if mask2 is None:
                print("Couldn't create CGImageRef for the mask data 2!")
    
            if invertedmask2 is None:
                print("Couldn't create CGImageRef for the inverted mask data 2!")
    
            return
    
        Quartz.CGContextScaleCTM(context, 1.5, 1.5)
        colorSpace = Utilities.getTheCalibratedRGBColorSpace()
        Quartz.CGContextSetFillColorSpace(context, colorSpace)
    
        # Set the fill color to a light blue.
        Quartz.CGContextSetFillColor(context, lightBlue)
        # Paint part of the background.
        backRect = Quartz.CGRectMake(width / 2, height / 2, width * 3, height)
        Quartz.CGContextFillRect(context, backRect)
    
        imageRect = Quartz.CGRectMake(0.0, height, width, height)
        Quartz.CGContextSaveGState(context)
        # Set the fill color to opaque black.
        Quartz.CGContextSetFillColor(context, black)
        # Mask 1.
        Quartz.CGContextDrawImage(context, imageRect, mask1)
    
        Quartz.CGContextTranslateCTM(context, width, 0)
        # Set the fill color to opaque red.
        Quartz.CGContextSetFillColor(context, darkRed)
        # Mask 2.
        Quartz.CGContextDrawImage(context, imageRect, mask2)
        Quartz.CGContextTranslateCTM(context, width, 0)
        # Set the fill color to dark orange.
        Quartz.CGContextSetFillColor(context, darkOrange)
        # Mask 3.
        Quartz.CGContextDrawImage(context, imageRect, mask1)
    
        Quartz.CGContextTranslateCTM(context, width, 0)
        # Make the orange 50% transparent.
        darkOrange[3] = 0.5
        Quartz.CGContextSetFillColor(context, darkOrange)
        # Mask 4.
        Quartz.CGContextDrawImage(context, imageRect, mask2)
        Quartz.CGContextRestoreGState(context)
    
        # Translate down the page. The cast is necessary
        # since height is typed as size_t which is unsigned.
        Quartz.CGContextTranslateCTM(context, 0, -height)
    
        # Set the fill color to an opaque green.
        Quartz.CGContextSetFillColor(context, darkGreen)
        # Mask 5.
        Quartz.CGContextDrawImage(context, imageRect, invertedmask2)
    
        Quartz.CGContextTranslateCTM(context, width, 0)
        # Set the fill color to a dark blue.
        Quartz.CGContextSetFillColor(context, darkBlue)
        # Mask 6.
        Quartz.CGContextDrawImage(context, imageRect, invertedmask1)
        Quartz.CGContextTranslateCTM(context, width, 0)
        # Set the fill color to purple.
        Quartz.CGContextSetFillColor(context, purple)
        # Mask 7.
        Quartz.CGContextDrawImage(context, imageRect, invertedmask2)
        Quartz.CGContextTranslateCTM(context, width, 0)
    
        # Make the purple 50% transparent.
        purple[3] = 0.5
        Quartz.CGContextSetFillColor(context, purple)
        # Mask 8.
        Quartz.CGContextDrawImage(context, imageRect, invertedmask1)
    
    
    def doMaskImageWithMaskFromURL(
        context,
        imageURL,
        imagewidth,
        imageheight,
        bitsPerComponent,
        theMaskingImageURL,
        maskwidth,
        maskheight,
    ):
        imageBitsPerPixel = bitsPerComponent * 3
        bytesPerRow = ((imagewidth * imageBitsPerPixel) + 7) / 8
        shouldInterpolate = True
        imageDataProvider = Quartz.CGDataProviderCreateWithURL(imageURL)
        if imageDataProvider is None:
            print("Couldn't create Image Data provider!")
            return
    
        colorspace = Utilities.getTheCalibratedRGBColorSpace()
        image = Quartz.CGImageCreate(
            imagewidth,
            imageheight,
            bitsPerComponent,
            imageBitsPerPixel,
            bytesPerRow,
            colorspace,
            Quartz.kCGImageAlphaNone,
            imageDataProvider,
            None,
            shouldInterpolate,
            Quartz.kCGRenderingIntentDefault,
        )
        del imageDataProvider
        if image is None:
            print("Couldn't create CGImageRef for this data!")
            return
    
        imageRect = Quartz.CGRectMake(0.0, imageheight, imagewidth, imageheight)
        # Draw the image.
        Quartz.CGContextDrawImage(context, imageRect, image)
    
        # Now the mask.
        maskDataProvider = Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)
        if maskDataProvider is None:
            print("Couldn't create Image Data provider!")
            return
    
        mask = Quartz.CGImageMaskCreate(
            maskwidth,
            maskheight,
            bitsPerComponent,
            bitsPerComponent,
            maskwidth,
            maskDataProvider,
            None,
            shouldInterpolate,
        )
        del maskDataProvider
        if mask is None:
            print("Couldn't create CGImageRef for mask data!")
            return
    
        # Draw the mask below the image. The current fill color (black)
        # is painted through the mask.
        maskRect = Quartz.CGRectMake(0.0, 0.0, maskwidth, maskheight)
        Quartz.CGContextDrawImage(context, maskRect, mask)
    
        # Create a new CGImage object, the image, masked with mask.
        imageMaskedWithImage = Quartz.CGImageCreateWithMask(image, mask)
        # Once the new image is created, the code can release the image
        # and the mask which make it up. Quartz retains what it needs
        # for the new masked image 'imageMaskedWithImage'.
        del image
        del mask
        if imageMaskedWithImage is None:
            print("Couldn't create image masked with mask!")
            return
    
        imageRect = Quartz.CGRectMake(
            imagewidth + 10, imageheight / 2, imagewidth, imageheight
        )
        # Draw the masked image to the right of the image and its mask.
        Quartz.CGContextDrawImage(context, imageRect, imageMaskedWithImage)
    
    
    def doMaskImageWithGrayImageFromURL(
        context,
        imageURL,
        imagewidth,
        imageheight,
        bitsPerComponent,
        theMaskingImageURL,
        maskwidth,
        maskheight,
    ):
        imageBitsPerPixel = bitsPerComponent * 3
        bytesPerRow = ((imagewidth * imageBitsPerPixel) + 7) / 8
        shouldInterpolate = True
    
        imageDataProvider = Quartz.CGDataProviderCreateWithURL(imageURL)
        if imageDataProvider is None:
            print("Couldn't create Image Data provider!")
            return
    
        colorspace = Utilities.getTheCalibratedRGBColorSpace()
        image = Quartz.CGImageCreate(
            imagewidth,
            imageheight,
            bitsPerComponent,
            imageBitsPerPixel,
            bytesPerRow,
            colorspace,
            Quartz.kCGImageAlphaNone,
            imageDataProvider,
            None,
            shouldInterpolate,
            Quartz.kCGRenderingIntentDefault,
        )
        del imageDataProvider
        if image is None:
            print("Couldn't create CGImageRef for this data!")
            return
    
        imageRect = Quartz.CGRectMake(0.0, imageheight, imagewidth, imageheight)
        # Draw the image.
        Quartz.CGContextDrawImage(context, imageRect, image)
    
        # Now the mask.
        maskDataProvider = Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)
        if maskDataProvider is None:
            print("Couldn't create Image Data provider!")
            return
    
        # The color space for the image MUST be DeviceGray for it to
        # be used as a masking image with CGImageCreateWithMask.
        deviceGraySpace = Quartz.CGColorSpaceCreateDeviceGray()
        mask = Quartz.CGImageCreate(
            maskwidth,
            maskheight,
            bitsPerComponent,
            bitsPerComponent,
            maskwidth,
            deviceGraySpace,
            Quartz.kCGImageAlphaNone,
            maskDataProvider,
            None,
            shouldInterpolate,
            Quartz.kCGRenderingIntentDefault,
        )
        # Release the color space since it is no longer needed.
        del deviceGraySpace
        del maskDataProvider
    
        if mask is None:
            print("Couldn't create CGImageRef for gray image data!")
            return
    
        # Draw the mask below the image. The current fill color (black)
        # is painted through the mask.
        maskRect = Quartz.CGRectMake(0.0, 0.0, maskwidth, maskheight)
        Quartz.CGContextDrawImage(context, maskRect, mask)
    
        # Create a new CGImage object, the image, masked with mask.
        imageMaskedWithImage = Quartz.CGImageCreateWithMask(image, mask)
    
        # Once the new image is created, the code can release the image
        # and the mask which make it up. Quartz retains what it needs
        # for the new masked image 'imageMaskedWithImage'.
        del image
        del mask
    
        if imageMaskedWithImage is None:
            print("Couldn't create image masked with mask!")
            return
    
        imageRect = Quartz.CGRectMake(
            imagewidth + 10, imageheight / 2, imagewidth, imageheight
        )
        # Draw the masked image to the right of the image and its mask.
        Quartz.CGContextDrawImage(context, imageRect, imageMaskedWithImage)
        # Be sure and release the masked image.
        del imageMaskedWithImage
    
    
    def doMaskImageWithColorFromURL(context, url, width, height, isColor):
        # This routine treats color images as RGB.
        bitsPerComponent = 8
        if isColor:
            bitsPerPixel = bitsPerComponent * 3
        else:
            bitsPerPixel = bitsPerComponent
    
        bytesPerRow = ((width * bitsPerPixel) + 7) / 8
        shouldInterpolate = True
    
        # This is a range of dark gray to black colors for an 8 bit per component
        # image in a gray or RGB color space. The entries are image sample
        # values of 0-0x1F for the first color component, 0-0x1F for the
        # second color component, and so on. For image sample values where
        # all components fall within the ranges in maskingColors, the sample
        # value is masked and therefore unpainted.
        maskingColors = (0x00, 0x1F, 0x00, 0x1F, 0x00, 0x1F)
        backColor = (1.0, 0.0, 0.0, 1.0)  # Opaque red.
    
        # Create a Quartz data provider from the supplied URL.
        dataProvider = Quartz.CGDataProviderCreateWithURL(url)
        if dataProvider is None:
            print("Couldn't create Image data provider!")
            return
    
        # Create an image of the specified width, height and bits per pixel
        # from the URL.
        if isColor:
            colorspace = Utilities.getTheCalibratedRGBColorSpace()
        else:
            colorspace = Utilities.getTheCalibratedGrayColorSpace()
    
        image = Quartz.CGImageCreate(
            width,
            height,
            bitsPerComponent,
            bitsPerPixel,
            bytesPerRow,
            colorspace,
            Quartz.kCGImageAlphaNone,
            dataProvider,
            None,
            shouldInterpolate,
            Quartz.kCGRenderingIntentDefault,
        )
        del dataProvider
        if image is None:
            print("Couldn't create CGImageRef for this data!")
            return
    
        imageRect = Quartz.CGRectMake(10.0, 10.0, width, height)
        # Quartz.CGContextScaleCTM(context, 0.33, 0.33)
        # Set the color space and the color, then
        # paint a red rectangle behind the image.
        Quartz.CGContextSetFillColorSpace(context, colorspace)
        Quartz.CGContextSetFillColor(context, backColor)
        Quartz.CGContextFillRect(context, imageRect)
        # Draw the image into the rectangle.
        Quartz.CGContextDrawImage(context, imageRect, image)
        # Create a new image from the original one, masking out a range
        # of the blackest sample values.
        imageMaskedWithColor = Quartz.CGImageCreateWithMaskingColors(image, maskingColors)
        # Release the original image; it is no longer needed.
        del image
        if imageMaskedWithColor is None:
            print("Couldn't create CGImageRef for masking color!")
            return
    
        # Paint the rectangle behind the next image with red.
        imageRect = Quartz.CGRectMake(30.0 + width, 10.0, width, height)
        Quartz.CGContextFillRect(context, imageRect)
        # Draw the image. Image sample values in the range of
        # the masking color are unpainted, allowing the background
        # to show through.
        Quartz.CGContextDrawImage(context, imageRect, imageMaskedWithColor)
    
    
    if 1:  # Set to 1 for code in the book.
    
        def drawWithClippingMask(context, theMaskingImageURL, imagewidth, imageheight):
            # An array of CGColor objects.
            colors = (
                Utilities.getRGBOpaqueDarkGreenColor(),
                Utilities.getRGBOpaqueDarkBlueColor(),
                Utilities.getRGBOpaqueBlueColor(),
                Utilities.getRGBOpaqueRedColor(),
            )
    
            imageBitsPerComponent = 8
            bytesPerRow = imagewidth
            shouldInterpolate = True
            decode = (1, 0)
    
            # Create the data.
            dataProvider = Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)
            if dataProvider is None:
                print("Couldn't create Image data provider!")
                return
    
            cs = Quartz.CGColorSpaceCreateDeviceGray()
            image = Quartz.CGImageCreate(
                imagewidth,
                imageheight,
                imageBitsPerComponent,
                imageBitsPerComponent,
                bytesPerRow,
                cs,
                Quartz.kCGImageAlphaNone,
                dataProvider,
                decode,
                shouldInterpolate,
                Quartz.kCGRenderingIntentDefault,
            )
            del cs
            del dataProvider
    
            if image is None:
                print("Couldn't create Image!")
                return
    
            imageRect = Quartz.CGRectMake(0, 0, imagewidth * 2 / 3, imageheight * 2 / 3)
    
            # Position for drawing the image at the left side of the figure.
            Quartz.CGContextTranslateCTM(context, 50, 50)
    
            # Draw the image.
            Quartz.CGContextDrawImage(context, imageRect, image)
    
            # Position to the right of the image just painted.
            Quartz.CGContextTranslateCTM(context, Quartz.CGRectGetWidth(imageRect) + 25, 0)
    
            # Clip to the image.
            Quartz.CGContextClipToMask(context, imageRect, image)
            # Release the image since this code no longer needs it.
            del image
    
            # Make a rect that has a width and height 1/3 that of the image.
            rect = Quartz.CGRectMake(
                0,
                0,
                Quartz.CGRectGetWidth(imageRect) / 3,
                Quartz.CGRectGetHeight(imageRect) / 3,
            )
    
            Quartz.CGContextTranslateCTM(context, 0, 2 * Quartz.CGRectGetHeight(rect))
    
            # Draw a 3 x 3 grid of rectangles, setting the color for each rectangle
            # by cycling through the array of CGColor objects in the 'colors' array.
            for j in range(3):
                Quartz.CGContextSaveGState(context)
                for i in range(3):
                    # Draw a row of rectangles.
                    # Set the fill color using one of the CGColor objects in the
                    # colors array.
                    Quartz.CGContextSetFillColorWithColor(context, colors[(i + j) % 4])
                    Quartz.CGContextFillRect(context, rect)
                    Quartz.CGContextTranslateCTM(context, Quartz.CGRectGetWidth(rect), 0)
    
                Quartz.CGContextRestoreGState(context)
                # Position to draw the next row.
                Quartz.CGContextTranslateCTM(context, 0, -Quartz.CGRectGetHeight(rect))
    
    else:
        # This code works just fine to screen but when drawing to a PDF
        # or printing context the masked drawing is completely masked out
        # due to a bug in Quartz prior to Tiger 10.4.3.
        def drawWithClippingMask(context, theMaskingImageURL, maskwidth, maskheight):
            # An array of Quartz.CGColor objects.
            colors = (
                Utilities.getRGBOpaqueDarkGreenColor(),
                Utilities.getRGBOpaqueDarkBlueColor(),
                Utilities.getRGBOpaqueBlueColor(),
                Utilities.getRGBOpaqueRedColor(),
            )
            maskBitsPerComponent = 8
            # bytesPerRow = ((maskwidth * maskBitsPerComponent) + 7) / 8
            shouldInterpolate = True
            maskDataProvider = Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)
    
            if maskDataProvider is None:
                print("Couldn't create Image Mask provider!")
                return
            mask = Quartz.CGImageMaskCreate(
                maskwidth,
                maskheight,
                maskBitsPerComponent,
                maskBitsPerComponent,
                maskwidth,
                maskDataProvider,
                None,
                shouldInterpolate,
            )
            del maskDataProvider
    
            if mask is None:
                print("Couldn't create Image Mask!")
                return
    
            maskRect = Quartz.CGRectMake(0, 0, maskwidth / 3, maskheight / 3)
    
            # Position for drawing the mask at the left side of the figure.
            Quartz.CGContextTranslateCTM(context, 50, 50)
            # Set the context fill color to a Quartz.CGColor object that is black.
            Quartz.CGContextSetFillColorWithColor(
                context, Utilities.getRGBOpaqueBlackColor()
            )
            # Draw the mask. It is painted with with the black fill color.
            Quartz.CGContextDrawImage(context, maskRect, mask)
    
            # Position to the right of the mask just painted.
            Quartz.CGContextTranslateCTM(context, Quartz.CGRectGetWidth(maskRect) + 25, 0)
    
            # Clip to the mask.
            Quartz.CGContextClipToMask(context, maskRect, mask)
            # Release the mask since this code no longer needs it.
            del mask
    
            # Make a rect that has a width and height 1/3 that of the image mask.
            rect = Quartz.CGRectMake(
                0,
                0,
                Quartz.CGRectGetWidth(maskRect) / 3,
                Quartz.CGRectGetHeight(maskRect) / 3,
            )
    
            Quartz.CGContextTranslateCTM(context, 0, 2 * Quartz.CGRectGetHeight(rect))
    
            # Draw a 3 x 3 grid of rectangles, setting the color for each rectangle
            # by cycling through the array of CGColor objects in the 'colors' array.
            for j in range(3):
                Quartz.CGContextSaveGState(context)
                for i in range(3):
                    # Draw a row of rectangles.
                    # Set the fill color using one of the CGColor objects in the
                    # colors array.
                    Quartz.CGContextSetFillColorWithColor(context, colors[(i + j) % 4])
                    Quartz.CGContextFillRect(context, rect)
                    Quartz.CGContextTranslateCTM(context, Quartz.CGRectGetWidth(rect), 0)
                Quartz.CGContextRestoreGState(context)
                # Position to draw the next row.
                Quartz.CGContextTranslateCTM(context, 0, -Quartz.CGRectGetHeight(rect))

.. rst-class:: tabbertab

Images.py
.........

.. sourcecode:: python

    import Cocoa
    import DataProvidersAndConsumers
    import Quartz
    import Utilities
    from LaunchServices import kUTTypePNG
    
    
    def drawJPEGImage(context, url):
        # Create a Quartz data provider for the supplied URL.
        jpgProvider = Quartz.CGDataProviderCreateWithURL(url)
        if jpgProvider is None:
            print("Couldn't create JPEG Data provider!")
            return
    
        # Create the CGImageRef for the JPEG image from the data provider.
        jpgImage = Quartz.CGImageCreateWithJPEGDataProvider(
            jpgProvider, None, True, Quartz.kCGRenderingIntentDefault
        )
    
        # CGImageCreateWithJPEGDataProvider retains the data provider.
        # Since this code created the data provider and this code no
        # longer needs it, it must release it.
        del jpgProvider
    
        if jpgImage is None:
            print("Couldn't create CGImageRef for JPEG data!")
            return
    
        # Make a rectangle that has its origin at (0,0) and
        # has a width and height that is 1/4 the native width
        # and height of the image.
        jpgRect = Quartz.CGRectMake(
            0.0,
            0.0,
            Quartz.CGImageGetWidth(jpgImage) / 4,
            Quartz.CGImageGetHeight(jpgImage) / 4,
        )
    
        # Draw the image into the rectangle.
        # This is Image 1.
        Quartz.CGContextDrawImage(context, jpgRect, jpgImage)
    
        Quartz.CGContextSaveGState(context)
    
        # Translate to the top-right corner of the image just drawn.
        Quartz.CGContextTranslateCTM(context, jpgRect.size.width, jpgRect.size.height)
        # Rotate by -90 degrees.
        Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(-90))
        # Translate in -x by the width of the drawing.
        Quartz.CGContextTranslateCTM(context, -jpgRect.size.width, 0)
    
        # Draw the image into the same rectangle as before.
        # This is Image 2.
        Quartz.CGContextDrawImage(context, jpgRect, jpgImage)
        Quartz.CGContextRestoreGState(context)
    
        Quartz.CGContextSaveGState(context)
    
        # Translate so that the next drawing of the image appears
        # below and to the right of the image just drawn.
        Quartz.CGContextTranslateCTM(
            context, jpgRect.size.width + jpgRect.size.height, jpgRect.size.height
        )
        # Scale the y axis by a negative value and flip the image.
        Quartz.CGContextScaleCTM(context, 0.75, -1.0)
        # This is Image 3.
        Quartz.CGContextDrawImage(context, jpgRect, jpgImage)
        Quartz.CGContextRestoreGState(context)
    
        # Adjust the position of the rectangle so that its origin is
        # to the right and above where Image 3 was drawn. Adjust the
        # size of the rectangle so that it is 1/4 the image width
        # and 1/6 the image height.
        jpgRect = Quartz.CGRectMake(
            1.75 * jpgRect.size.width + jpgRect.size.height,
            jpgRect.size.height,
            Quartz.CGImageGetWidth(jpgImage) / 4,
            Quartz.CGImageGetHeight(jpgImage) / 6,
        )
        # This is Image 4.
        Quartz.CGContextDrawImage(context, jpgRect, jpgImage)
    
    
    def drawImageFromURL(context, url, width, height, bitsPerComponent, isRGB):
        # This routine treats color images as RGB
        if isRGB:
            bitsPerPixel = bitsPerComponent * 3
        else:
            bitsPerPixel = bitsPerComponent
    
        bytesPerRow = (width * bitsPerPixel + 7) / 8
        shouldInterpolate = True
    
        # Create a Quartz data provider from the supplied URL.
        dataProvider = Quartz.CGDataProviderCreateWithURL(url)
        if dataProvider is None:
            print("Couldn't create Image data provider!")
            return
    
        # Get a Quartz color space object appropriate for the image type.
        if isRGB:
            colorspace = Utilities.getTheCalibratedRGBColorSpace()
        else:
            colorspace = Utilities.getTheCalibratedGrayColorSpace()
    
        # Create an image of the width, height, and bitsPerComponent with
        # no alpha data, the default decode array, with interpolation,
        # and the default rendering intent for images. This code is
        # intended for Gray images of the format GGGGG... or RGB images
        # of the format RGBRGBRGB... .
        image = Quartz.CGImageCreate(
            width,
            height,
            bitsPerComponent,
            bitsPerPixel,
            bytesPerRow,
            colorspace,
            Quartz.kCGImageAlphaNone,
            dataProvider,
            None,
            shouldInterpolate,
            Quartz.kCGRenderingIntentDefault,
        )
        # Quartz retains the data provider with the image and since this
        # code does not create any more images with the data provider, it
        # can release it.
        del dataProvider
        if image is None:
            print("Couldn't create CGImageRef for this data!")
            return
    
        # Create a rectangle into which the code will draw the image.
        imageRect = Quartz.CGRectMake(0.0, 0.0, width, height)
    
        # Draw the image into the rectangle.
        Quartz.CGContextDrawImage(context, imageRect, image)
    
    
    def doColorRampImage(context):
        width = 256
        height = 256
        bitsPerComponent = 8
        bitsPerPixel = 24
        bytesPerRow = width * 3
        shouldInterpolate = True
    
        imageDataProvider = DataProvidersAndConsumers.createRGBRampDataProvider()
        if imageDataProvider is None:
            print("Couldn't create Image Data provider!")
            return
    
        colorspace = Utilities.getTheCalibratedRGBColorSpace()
        image = Quartz.CGImageCreate(
            width,
            height,
            bitsPerComponent,
            bitsPerPixel,
            bytesPerRow,
            colorspace,
            Quartz.kCGImageAlphaNone,
            imageDataProvider,
            None,
            shouldInterpolate,
            Quartz.kCGRenderingIntentDefault,
        )
        # No longer need the data provider.
        del imageDataProvider
        if image is None:
            print("Couldn't create CGImageRef for this data!")
            return
    
        imageRect = Quartz.CGRectMake(0.0, 0.0, width, height)
        # Draw the image.
        Quartz.CGContextDrawImage(context, imageRect, image)
    
    
    def doImageWithCallbacksCreatedFromURL(
        context, url, width, height, bitsPerComponent, isRGB
    ):
        if isRGB:
            bitsPerPixel = bitsPerComponent * 3
        else:
            bitsPerPixel = bitsPerComponent
    
        bytesPerRow = ((width * bitsPerPixel) + 7) / 8
        shouldInterpolate = True
    
        dataProvider = DataProvidersAndConsumers.createSequentialAccessDPForURL(url)
        if dataProvider is None:
            print("Couldn't create Image Data provider!")
            return
    
        # Create a Quartz color space object appropriate for the image type.
        # These user written functions create the color space object
        # and that reference must be released by this code.
        if isRGB:
            colorspace = Utilities.getTheCalibratedRGBColorSpace()
        else:
            colorspace = Utilities.getTheCalibratedGrayColorSpace()
    
        image = Quartz.CGImageCreate(
            width,
            height,
            bitsPerComponent,
            bitsPerPixel,
            bytesPerRow,
            colorspace,
            Quartz.kCGImageAlphaNone,
            dataProvider,
            None,
            shouldInterpolate,
            Quartz.kCGRenderingIntentDefault,
        )
        del dataProvider
        if image is None:
            print("Couldn't create CGImageRef for this data!")
            return
    
        imageRect = Quartz.CGRectMake(0.0, 0.0, width, height)
    
        # Draw the image into the rectangle.
        Quartz.CGContextDrawImage(context, imageRect, image)
    
    
    def doGrayRamp(context):
        width = 256
        height = 1
        bitsPerComponent = 8
        bitsPerPixel = 8
        bytesPerRow = width
        shouldInterpolate = True
    
        dataProvider = DataProvidersAndConsumers.createGrayRampDirectAccessDP()
        if dataProvider is None:
            print("Couldn't create Gray Ramp provider!")
            return
    
        colorspace = Utilities.getTheCalibratedGrayColorSpace()
        image = Quartz.CGImageCreate(
            width,
            height,
            bitsPerComponent,
            bitsPerPixel,
            bytesPerRow,
            colorspace,
            Quartz.kCGImageAlphaNone,
            dataProvider,
            None,
            shouldInterpolate,
            Quartz.kCGRenderingIntentDefault,
        )
        del dataProvider
        if image is None:
            print("Couldn't create CGImageRef for image data!")
            return
    
        imageRect = Quartz.CGRectMake(0.0, 0.0, 256, 256)
        # Drawing the image that is 256 samples wide and
        # 1 scanline high into a rectangle that is 256 x 256 units
        # on a side causes Quartz to stretch the image to fill
        # the destination rectangle.
        Quartz.CGContextDrawImage(context, imageRect, image)
    
    
    # This routine examines the CGImageSource at index 0 to
    # determine if the first image is a floating point image and
    # if it is, it returns an options dictionary suitable for
    # passing to CGImageSourceCreateImageAtIndex in order to create
    # a CGImageRef that contains full dynamic range floating point data.
    def createFloatingPointImageOptions(imageSource):
        # Allow the image to be a floating point image.
        # Without this, Quartz would return integer pixel data, even for
        # floating point images. Typically you don't need floating point data
        # but in some special cases you might want it.
        options = {Quartz.kCGImageSourceShouldAllowFloat: True}
        isFloat = False
    
        # Obtain the properties for the first image
        # in the image source. This is a 'Copy' function
        # so the code owns a reference to the
        # dictionary returned.
        properties = Quartz.CGImageSourceCopyPropertiesAtIndex(imageSource, 0, options)
        if properties is not None:
            # Get the value for the kCGImagePropertyIsFloat if it exists
            # and if the value is a CFBoolean then get the corresponding
            # Boolean result.
            if Quartz.kCGImagePropertyIsFloat in properties:
                isFloat = bool(properties[Quartz.kCGImagePropertyIsFloat])
    
        if not isFloat:
            return None
    
        return options
    
    
    def myCreateImageUsingImageSource(url):
        # Set to zero, indicating the property was unavailable.
        xdpi = ydpi = 0
    
        # Create the image source from the URL.
        imageSource = Quartz.CGImageSourceCreateWithURL(url, None)
        if imageSource is None:
            print("Couldn't create image source from URL!")
            return (None, xdpi, ydpi)
    
        if False:
            options = createFloatingPointImageOptions(imageSource)
            if options is not None:
                print("image IS a floating point image")
            else:
                print("image IS NOT a floating point image")
        else:
            options = None
    
        # Obtain the properties dictionary for the first image
        # in the image source. This is a copy function so this
        # code owns the reference returned and must
        # must release it.
        properties = Quartz.CGImageSourceCopyPropertiesAtIndex(imageSource, 0, options)
        if properties is not None:
            # Check for the x and y resolution of the image.
            xdpi = properties[Quartz.kCGImagePropertyDPIWidth]
            ydpi = properties[Quartz.kCGImagePropertyDPIHeight]
    
        # Create a CGImageRef from the first image in the CGImageSource.
        image = Quartz.CGImageSourceCreateImageAtIndex(imageSource, 0, options)
        # Release the CGImageSource object since it is no longer needed
        # and this code created it. This code uses CFRelease since a
        # CGImageSource object is a CoreFoundation object.
        del imageSource
        del options
    
        if image is None:
            print("Couldn't create image from image source!")
            return None
    
        return (image, xdpi, ydpi)
    
    
    def myCreateThumbnailFromImageSource(url):
        maxThumbSize = 160
    
        # Create the image source from the URL.
        imageSource = Quartz.CGImageSourceCreateWithURL(url, None)
        if imageSource is None:
            print("Couldn't create image source from URL!")
            return None
    
        options = {
            # Specify 160 pixels as the maximum width and height of
            # the thumbnail for Quartz to create.
            Quartz.kCGImageSourceThumbnailMaxPixelSize: maxThumbSize,
            # Request that Quartz create a thumbnail image if
            # thumbnail data isn't present in the file.
            Quartz.kCGImageSourceCreateThumbnailFromImageIfAbsent: True,
        }
    
        # Create the thumbnail image for the first image in the
        # image source, that at index 0, using the options
        # dictionary that the code just created.
        thumb = Quartz.CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options)
    
        # Release the options dictionary.
        del options
        # Release the image source the code created.
        del imageSource
    
        if thumb is None:
            print("Couldn't create thumbnail from image source!")
            return None
    
        return thumb
    
    
    def imageHasFloatingPointSamples(image):
        if hasattr(Quartz, "CGImageGetBitmapInfo"):
            return (
                Quartz.kCGBitmapFloatComponents & Quartz.CGImageGetBitmapInfo(image)
            ) != 0
        return False
    
    
    def drawImageWithCGImageDataSource(context, url):
        # This code would be better if it created the image source
        # once and used the same image source to create the image and its
        # thumbnail, but the point here is to simply test the routines
        # myCreateImageUsingImageSource and myCreateThumbnailFromImageSource.
    
        image, xdpi, ydpi = myCreateImageUsingImageSource(url)
        if image is None:
            print("myCreateImageFromImageSource didn't create a CGImage!")
            return
    
        print(f"xdpi = {xdpi:2.f}, ydpi = {ydpi:2.f}")
        imageRect = Quartz.CGRectMake(
            0.0, 0.0, Quartz.CGImageGetWidth(image) / 3, Quartz.CGImageGetHeight(image) / 3
        )
        Quartz.CGContextDrawImage(context, imageRect, image)
    
        if 0:
            isFloatingImage = imageHasFloatingPointSamples(image)
            if isFloatingImage:
                print("First image IS a floating point image")
            else:
                print("First image IS NOT a floating point image")
    
        del image
    
        image = myCreateThumbnailFromImageSource(url)
        if image is None:
            print("myCreateThumbnailFromImageSource didn't create a CGImage!")
            return
    
        imageRect = Quartz.CGRectMake(
            400.0, 0.0, Quartz.CGImageGetWidth(image), Quartz.CGImageGetHeight(image)
        )
        Quartz.CGContextDrawImage(context, imageRect, image)
    
        del image
    
    
    class MyIncrementalData:
        data = None
        dataSize = 0
        repCount = 0
        chunkSize = 0
    
    
    # This is a dummy data accumulation routine used to demonstrate incremental
    # loading of an image.
    def myCreateAccumulatedDataSoFar(myDataP):
        myDataP.repCount += 1
        sizeToReturn = myDataP.chunkSize * myDataP.repCount
    
        if sizeToReturn > myDataP.dataSize:
            sizeToReturn = myDataP.dataSize
    
        done = sizeToReturn == myDataP.dataSize
        data = Cocoa.CFDataCreate(None, myDataP.data, sizeToReturn)
        return data, done
    
    
    def MyDrawIncrementalImage(context, image, fullHeight):
        # Obtain the width and height of the image that has been
        # accumulated so far.
        print("MyDrawIncrementalImage", context, image, fullHeight)
        width = Quartz.CGImageGetWidth(image)
        height = Quartz.CGImageGetHeight(image)
        # Adjust the location of the imageRect so that the origin is
        # such that the full image would be located at 0,0 and the partial
        # image top-left corner does not move as the image is filled in.
        # This is only needed for views where the y axis points up the
        # drawing canvas.
        imageRect = Quartz.CGRectMake(0, fullHeight - height, width, height)
        Quartz.CGContextDrawImage(context, imageRect, image)
    
    
    def myDrawFirstImageIncrementally(context, myDataP):
        height = -1
        # Create an incremental image source.
        imageSource = Quartz.CGImageSourceCreateIncremental(None)
        if imageSource is None:
            print("Couldn't create incremental imagesource!")
            return
    
        # Loop, gathering the necessary data to find the True
        # height of the image.
        while 1:
            # Fetch the data. The CFData object returned by
            # myCreateAccumulatedDataSoFar is used to update the
            # image source. When the data is complete, the code
            # passes True in the 'done' parameter passed to
            # CGImageSourceUpdateData. Once the data is passed
            # to CGImageSourceUpdateData, the code can release
            # its reference to the data.
    
            # Accumulate the data.
            data, done = myCreateAccumulatedDataSoFar(myDataP)
            Quartz.CGImageSourceUpdateData(imageSource, data, done)
    
            # Release the data since Quartz retains it and this code
            # no longer needs it.
            del data
    
            if height < 0:
                print("height < 0", height)
                # Determine the height of the full image. This is needed in order
                # to adjust the location of the drawing of the partial image in
                # a context where the y axis has the default Quartz orientation
                # pointing up the drawing canvas.
                properties = Quartz.CGImageSourceCopyPropertiesAtIndex(imageSource, 0, None)
                if properties is not None:
                    if Quartz.kCGImagePropertyPixelHeight in properties:
                        height = properties[Quartz.kCGImagePropertyPixelHeight]
                del properties
    
            # Once the height is obtained, go ahead and see if Quartz
            # has enough data to create a CGImage object.
            print("height", height)
            if height > 0:
                # Now create the CGImageRef from the image source for the
                # first image.
                image = Quartz.CGImageSourceCreateImageAtIndex(imageSource, 0, None)
                if image is not None:
                    # Draw the image using the height of the full image
                    # to adjust the location where the image is drawn.
                    MyDrawIncrementalImage(context, image, height)
                    # Release the partial image once you've drawn it.
                    del image
                    # Potentially you would want to flush the context so
                    # that drawing to a window would appear, even inside
                    # this loop. Of course this flush should really be
                    # done on a timer so that the flush only occurs at
                    # most every 60th of a second. See Chapter 17 regarding
                    # timing your usage of CGContextFlush.
                    Quartz.CGContextFlush(context)
    
            # Obtain the status for the image source for the first image.
            status = Quartz.CGImageSourceGetStatusAtIndex(imageSource, 0)
    
            if done:  # or status  == Quartz.kCGImageStatusComplete:
                print(done, status, status == Quartz.kCGImageStatusComplete)
                break
    
    
    def createMyIncrementalDataFromURL(url, myDataP):
        myDataP.data = None
        myDataP.dataSize = 0
        myDataP.repCount = 0
    
        success, pathString = Cocoa.CFURLGetFileSystemRepresentation(url, True, None, 1024)
        pathString = pathString.rstrip(b"\0")
    
        if success and len(pathString):
            fp = open(pathString, "rb")
            myDataP.data = fp.read()
            fp.close()
            myDataP.dataSize = len(myDataP.data)
    
        if myDataP.dataSize > 0:
            myDataP.chunkSize = myDataP.dataSize / 10  # 10 chunks
    
    
    def doIncrementalImageWithURL(context, url):
        myData = MyIncrementalData()
        createMyIncrementalDataFromURL(url, myData)
        if myData.data is None:
            print("couldn't read data from URL!")
    
        myDrawFirstImageIncrementally(context, myData)
        del myData
    
    
    # This code requires QuickTime.framework.
    # from Carbon import Qt
    def createCGImageWithQuickTimeFromURL(url):
        """
        Note: this function doesn't actually worked because the APIs used in here
        aren't properly wrapped (yet).
        """
        return None
    
        imageRef = None
    
        err = 0
        result, dataRef, dataRefType = Quartz.QTNewDataReferenceFromCFURL(
            url, 0, None, None
        )
        if dataRef is not None:
            err, gi = Quartz.GetGraphicsImporterForDataRefWithFlags(
                dataRef, dataRefType, None, 0
            )
            if not err and gi:
                # Tell the graphics importer that it shouldn't perform
                # gamma correction and it should create an image in
                # the original source color space rather than matching it to
                # a generic calibrated color space.
                result = Quartz.GraphicsImportSetFlags(
                    gi,
                    (
                        Quartz.kGraphicsImporterDontDoGammaCorrection
                        + Quartz.kGraphicsImporterDontUseColorMatching
                    ),
                )
                if result == 0:
                    result, imageRef = Quartz.GraphicsImportCreateCGImage(gi, None, 0)
                    if result != 0:
                        print("got a bad result = %d!" % (result,))
                Quartz.DisposeHandle(dataRef)
                Quartz.CloseComponent(gi)
    
        return imageRef
    
    
    def drawQTImageWithQuartz(context, url):
        image = createCGImageWithQuickTimeFromURL(url)
        if image is None:
            print("createCGImageWithQuickTimeFromURL didn't create a CGImage!")
            return
    
        imageRect = Quartz.CGRectMake(
            0.0, 0.0, Quartz.CGImageGetWidth(image), Quartz.CGImageGetHeight(image)
        )
        Quartz.CGContextDrawImage(context, imageRect, image)
    
    
    def drawJPEGDocumentWithMultipleProfiles(context, url):
        # isDeviceRGBImage = False
    
        # Create a Quartz data provider for the supplied URL.
        jpgProvider = Quartz.CGDataProviderCreateWithURL(url)
        if jpgProvider is None:
            print("Couldn't create JPEG Data provider!")
            return
    
        # Create the Quartz.CGImageRef for the JPEG image from the data provider.
        jpgImage = Quartz.CGImageCreateWithJPEGDataProvider(
            jpgProvider, None, True, Quartz.kCGRenderingIntentDefault
        )
        del jpgProvider
        if jpgImage is None:
            print("Couldn't create CGImageRef for JPEG data!")
            return
    
        # Get the color space characterizing the image. This is a
        # function with 'Get' semantics so the code doesn't own a reference
        # to the color space returned and must not release it.
        originalColorSpace = Quartz.CGImageGetColorSpace(jpgImage)
        if originalColorSpace is None:
            print("image is a masking image, not an image with color!")
            return
    
        if Quartz.CGColorSpaceGetNumberOfComponents(originalColorSpace) != 3:
            print("This example only works with 3 component JPEG images")
            return
    
        # Determine if the original color space is DeviceRGB. If that is
        # not the case then bail.
        # comparisonColorSpace = Quartz.CGColorSpaceCreateDeviceRGB()
    
        # Note that this comparison of color spaces works only on
        # Jaguar and later where a CGColorSpaceRef is a
        # CoreFoundation object. Otherwise this will crash!
        #
        # NOTE: 20140109: Disabled the color space comparison because that's not valid
        #       on recent enough OSX versions.
    
        # isDeviceRGBImage = (comparisonColorSpace == originalColorSpace)
    
        # This code created 'comparisonColorSpace' so it must release it.
        # del comparisonColorSpace
    
        # if not isDeviceRGBImage:
        #    print("The color space for the JPEG image is not DeviceRGB!",
        #       comparisonColorSpace, originalColorSpace)
        #    #return
    
        # Might need to adjust this based on the size of the original image.
        Quartz.CGContextScaleCTM(context, 0.5, 0.5)
    
        imageRect = Quartz.CGRectMake(
            0.0,
            Quartz.CGImageGetHeight(jpgImage) / 2,
            Quartz.CGImageGetWidth(jpgImage),
            Quartz.CGImageGetHeight(jpgImage),
        )
    
        # Draw the original image to the left of the other two.
        Quartz.CGContextDrawImage(context, imageRect, jpgImage)
    
        # Recharacterize the original image with the generic Calibrated RGB
        # color space.
        updatedImage1 = Quartz.CGImageCreateCopyWithColorSpace(
            jpgImage, Utilities.getTheCalibratedRGBColorSpace()
        )
        # Release the original image since this code is done with it.
        del jpgImage
        if updatedImage1 is None:
            print("There is no updated image to draw!")
            return
    
        # Draw the image characterized by the Generic profile
        # to the right of the other image.
        imageRect = Quartz.CGRectOffset(imageRect, Quartz.CGRectGetWidth(imageRect) + 10, 0)
        Quartz.CGContextDrawImage(context, imageRect, updatedImage1)
    
        # Recharacterize the image but now with a color space
        # created with the sRGB profile.
        updatedImage2 = Quartz.CGImageCreateCopyWithColorSpace(
            updatedImage1, Utilities.getTheSRGBColorSpace()
        )
        # Release updatedImage1 since this code is done with it.
        del updatedImage1
        if updatedImage2 is None:
            print("There is no second updated image to draw!")
            return
    
        # Draw the image characterized by the sRGB profile to the right of
        # the image characterized by the generic RGB profile.
        imageRect = Quartz.CGRectOffset(imageRect, Quartz.CGRectGetWidth(imageRect) + 10, 0)
        Quartz.CGContextDrawImage(context, imageRect, updatedImage2)
    
    
    def createRedGreenRampImageData(width, height, size):
        try:
            dataP = bytearray(size)
        except MemoryError:
            return None
    
        idx = 0
        # Build an image that is RGB 24 bits per sample. This is a ramp
        # where the red component value increases in red from left to
        # right and the green component increases from top to bottom.
        for g in range(height):
            for r in range(width):
                dataP[idx + 0] = r
                dataP[idx + 1] = g
                dataP[idx + 2] = 0
                idx += 3
    
        return dataP
    
    
    def createRGBRampSubDataProvider(subRect):
        bytesPerSample = 3
        width = 256
        height = 256
        bytesPerRow = width * bytesPerSample
        startOffsetX = subRect.origin.x
        startOffsetY = subRect.origin.y
        imageDataSize = bytesPerRow * height
    
        # The first image sample is at
        # (startOffsetY*bytesPerRow + startOffsetX*bytesPerSample)
        # bytes into the RGB ramp data.
        firstByteOffset = startOffsetY * bytesPerRow + startOffsetX * bytesPerSample
    
        # The actual size of the image data provided is the full image size
        # minus the amount skipped at the beginning. This is more than the
        # total amount of data that is needed for the subimage but it is
        # valid and easy to calculate.
        totalBytesProvided = imageDataSize - firstByteOffset
    
        # Create the full color ramp.
        dataP = createRedGreenRampImageData(width, height, imageDataSize)
        if dataP is None:
            print("Couldn't create image data!")
            return None
    
        # Use the pointer to the first byte as the info parameter since
        # that is the pointer to the block to free when done.
        dataProvider = Quartz.CGDataProviderCreateWithData(
            dataP, dataP[firstByteOffset:], totalBytesProvided, None
        )
    
        if dataProvider is None:
            return None
    
        return dataProvider
    
    
    def doColorRampSubImage(context):
        # Start 4 scanlines from the top and 16 pixels from the left edge,
        # skip the last 40 scanlines of the image and the right
        # most 64 pixels.
        insetLeft = 16
        insetTop = 4
        insetRight = 64
        insetBottom = 40
    
        fullImageWidth = 256
        fullImageHeight = 256
        subImageWidth = fullImageWidth - insetLeft - insetRight
        subImageHeight = fullImageHeight - insetTop - insetBottom
        bitsPerComponent = 8
        bitsPerPixel = 24
        bytesPerRow = fullImageWidth * 3
        shouldInterpolate = True
    
        imageSubRect = Quartz.CGRectMake(insetLeft, insetTop, subImageWidth, subImageHeight)
        colorspace = Utilities.getTheCalibratedRGBColorSpace()
    
        if hasattr(Quartz, "CGImageCreateWithImageInRect"):
            imageDataProvider = DataProvidersAndConsumers.createRGBRampDataProvider()
            if imageDataProvider is None:
                print("Couldn't create Image Data provider!")
                return
    
            fullImage = Quartz.CGImageCreate(
                fullImageWidth,
                fullImageHeight,
                bitsPerComponent,
                bitsPerPixel,
                bytesPerRow,
                colorspace,
                Quartz.kCGImageAlphaNone,
                imageDataProvider,
                None,
                shouldInterpolate,
                Quartz.kCGRenderingIntentDefault,
            )
            if fullImage is not None:
                image = Quartz.CGImageCreateWithImageInRect(fullImage, imageSubRect)
                # release the full image since it is no longer required.
                del fullImage
    
        # If the image hasn't been created yet, this code uses the
        # customized data provider to do so.
        if image is None:
            imageDataProvider = createRGBRampSubDataProvider(imageSubRect)
            if imageDataProvider is None:
                print("Couldn't create Image Data provider!")
                return
    
            # By supplying bytesPerRow, the extra data at the end of
            # each scanline and the beginning of the next is properly skipped.
            image = Quartz.CGImageCreate(
                subImageWidth,
                subImageHeight,
                bitsPerComponent,
                bitsPerPixel,
                bytesPerRow,
                colorspace,
                Quartz.kCGImageAlphaNone,
                imageDataProvider,
                None,
                shouldInterpolate,
                Quartz.kCGRenderingIntentDefault,
            )
    
        # This code no longer needs the data provider.
        del imageDataProvider
    
        if image is None:
            print("Couldn't create CGImageRef for this data!")
            return
    
        # Draw the subimage.
        rect = Quartz.CGRectMake(0, 0, subImageWidth, subImageHeight)
        Quartz.CGContextDrawImage(context, rect, image)
    
    
    def exportCGImageToPNGFileWithDestination(image, url):
        resolution = 144.0
    
        # Create an image destination at the supplied URL that
        # corresponds to the PNG image format.
        imageDestination = Quartz.CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, None)
    
        if imageDestination is None:
            print("couldn't create image destination!")
            return
    
        # Set the keys to be the x and y resolution of the image.
        options = {
            Quartz.kCGImagePropertyDPIWidth: resolution,
            Quartz.kCGImagePropertyDPIHeight: resolution,
        }
    
        # Add the image with the options dictionary to the destination.
        Quartz.CGImageDestinationAddImage(imageDestination, image, options)
    
        # Release the options dictionary this code created.
        del options
    
        # When all the images are added to the destination, finalize it.
        Quartz.CGImageDestinationFinalize(imageDestination)
    
        # Release the destination when done with it.
        del imageDestination
    
    
    # This code requires QuickTime.framework
    #   include <QuickTime/QuickTime.h>
    def exportCGImageToJPEGFile(imageRef, url):
        # This doesn't actually work due to lame Python Quicktime bindings...
        return
    
        result, dataRef, dataRefType = Quartz.QTNewDataReferenceFromCFURL(
            url, 0, None, None
        )
        if result == 0:
            result, graphicsExporter = Quartz.OpenADefaultComponent(
                Quartz.GraphicsExporterComponentType, Quartz.kQTFileTypeJPEG
            )
            if result == 0:
                result = Quartz.GraphicsExportSetInputCGImage(graphicsExporter, imageRef)
                if result == 0:
                    result = Quartz.GraphicsExportSetOutputDataReference(
                        graphicsExporter, dataRef, dataRefType
                    )
                if result == 0:
                    result, sizeWritten = Quartz.GraphicsExportDoExport(
                        graphicsExporter, None
                    )
    
                Quartz.CloseComponent(graphicsExporter)
    
        if dataRef is not None:
            Quartz.DisposeHandle(dataRef)
    
        if result != 0:
            print("Exporting QT image got bad result = %d!" % (result,))
    
    
    def exportColorRampImageWithQT(context):
        width = 256
        height = 256
        bitsPerComponent = 8
        bitsPerPixel = 24
        bytesPerRow = width * 3
        shouldInterpolate = True
    
        imageDataProvider = DataProvidersAndConsumers.createRGBRampDataProvider()
        if imageDataProvider is None:
            print("Couldn't create Image Data provider!")
            return
    
        colorspace = Utilities.getTheCalibratedRGBColorSpace()
        image = Quartz.CGImageCreate(
            width,
            height,
            bitsPerComponent,
            bitsPerPixel,
            bytesPerRow,
            colorspace,
            Quartz.kCGImageAlphaNone,
            imageDataProvider,
            None,
            shouldInterpolate,
            Quartz.kCGRenderingIntentDefault,
        )
        del imageDataProvider
        if image is None:
            print("Couldn't create CGImageRef for this data!")
            return
    
        rect = Quartz.CGRectMake(0.0, 0.0, width, height)
        Quartz.CGContextDrawImage(context, rect, image)
    
        # Of course this is a total hack.
        outPath = b"/tmp/imageout.jpg"
        exportURL = Cocoa.CFURLCreateFromFileSystemRepresentation(
            None, outPath, len(outPath), False
        )
        if exportURL:
            exportCGImageToJPEGFile(image, exportURL)

.. rst-class:: tabbertab

MyAppController.py
..................

.. sourcecode:: python

    import BitmapContext
    import Cocoa
    import objc
    import PDFHandling
    import Utilities
    
    # Initial defaults
    _dpi = 144
    _useQT = False
    
    
    def getURLToExport(suffix):
        savePanel = Cocoa.NSSavePanel.savePanel()
    
        initialFileName = f"BasicDrawing.{suffix}"
    
        if (
            savePanel.runModalForDirectory_file_(None, initialFileName)
            == Cocoa.NSFileHandlingPanelOKButton
        ):
            return savePanel.URL()
    
        return None
    
    
    class MyAppController(Cocoa.NSObject):
        theView = objc.IBOutlet()
        currentDPIMenuItem = objc.IBOutlet()
        currentExportStyleMenuItem = objc.IBOutlet()
    
        @objc.IBAction
        def print_(self, sender):
            self.theView.print_(sender)
    
        def updateDPIMenu_(self, sender):
            if self.currentDPIMenuItem is not sender:
                # Uncheck the previous item.
                if self.currentDPIMenuItem is not None:
                    self.currentDPIMenuItem.setState_(Cocoa.NSOffState)
                # Update to the current item.
                self.currentDPIMenuItem = sender
                # Check new menu item.
                self.currentDPIMenuItem.setState_(Cocoa.NSOnState)
    
        def updateExportStyleMenu_(self, sender):
            if self.currentExportStyleMenuItem is not sender:
                # Uncheck the previous item.
                if self.currentExportStyleMenuItem is not None:
                    self.currentExportStyleMenuItem.setState_(Cocoa.NSOffState)
                # Update to the current item.
                self.currentExportStyleMenuItem = sender
                # Check new menu item.
                self.currentExportStyleMenuItem.setState_(Cocoa.NSOnState)
    
        @objc.IBAction
        def setExportResolution_(self, sender):
            global _dpi
            _dpi = sender.tag()
            self.updateDPIMenu_(sender)
    
        @objc.IBAction
        def setUseQT_(self, sender):
            global _useQT
            _useQT = True
            self.updateExportStyleMenu_(sender)
    
        @objc.IBAction
        def setUseCGImageSource_(self, sender):
            global _useQT
            _useQT = False
            self.updateExportStyleMenu_(sender)
    
        def setupExportInfo_(self, exportInfoP):
            # Use the printable version of the current command. This produces
            # the best results for exporting.
            exportInfoP.command = self.theView.currentPrintableCommand()
            exportInfoP.fileType = "    "  # unused
            exportInfoP.useQTForExport = _useQT
            exportInfoP.dpi = _dpi
    
        @objc.IBAction
        def exportAsPDF_(self, sender):
            url = getURLToExport("pdf")
            if url is not None:
                exportInfo = Utilities.ExportInfo()
                self.setupExportInfo_(exportInfo)
                PDFHandling.MakePDFDocument(url, exportInfo)
    
        @objc.IBAction
        def exportAsPNG_(self, sender):
            url = getURLToExport("png")
            if url is not None:
                exportInfo = Utilities.ExportInfo()
                self.setupExportInfo_(exportInfo)
                BitmapContext.MakePNGDocument(url, exportInfo)
    
        @objc.IBAction
        def exportAsTIFF_(self, sender):
            url = getURLToExport("tif")
            if url is not None:
                exportInfo = Utilities.ExportInfo()
                self.setupExportInfo_(exportInfo)
                BitmapContext.MakeTIFFDocument(url, exportInfo)
    
        @objc.IBAction
        def exportAsJPEG_(self, sender):
            url = getURLToExport("jpg")
            if url is not None:
                exportInfo = Utilities.ExportInfo()
                self.setupExportInfo_(exportInfo)
                BitmapContext.MakeJPEGDocument(url, exportInfo)
    
        def validateMenuItem_(self, menuItem):
            if menuItem.tag == _dpi:
                self.currentDPIMenuItem = menuItem
                menuItem.setState_(True)
            elif menuItem.action() == "setUseQT:":
                if _useQT:
                    self.currentDPIMenuItem = menuItem
                    menuItem.setState_(True)
                else:
                    menuItem.setState_(False)
    
            elif menuItem.action() == "setUseCGImageSource:":
                if _useQT:
                    self.currentDPIMenuItem = menuItem
                    menuItem.setState_(True)
                else:
                    menuItem.setState_(False)
    
            return True

.. rst-class:: tabbertab

MyView.py
.........

.. sourcecode:: python

    import AppDrawing
    import Cocoa
    import Quartz
    import FrameworkTextDrawing
    import FrameworkUtilities
    import objc
    import UIHandling
    import PDFHandling
    from objc import super  # noqa: A004
    
    # XXX: Why are these global?
    _drawingCommand = UIHandling.kHICommandSimpleRect
    _pdfDocument = None
    
    
    class MyView(Cocoa.NSView):
        currentMenuItem = objc.IBOutlet()
    
        def initWithFrame_(self, frameRect):
            self = super().initWithFrame_(frameRect)
            if self is None:
                return None
    
            global _pdfDocument
            _pdfDocument = None
            return self
    
        if False:
    
            def isFlipped(self):
                return True
    
        def drawRect_(self, rect):
            context = Cocoa.NSGraphicsContext.currentContext().graphicsPort()
    
            if _pdfDocument is None:
                if _drawingCommand in (
                    UIHandling.kHICommandDrawNSString,
                    UIHandling.kHICommandDrawNSLayoutMgr,
                    UIHandling.kHICommandDrawCustomNSLayoutMgr,
                ):
                    if _drawingCommand == UIHandling.kHICommandDrawNSString:
                        FrameworkTextDrawing.drawNSStringWithAttributes()
    
                    elif _drawingCommand == UIHandling.kHICommandDrawNSLayoutMgr:
                        FrameworkTextDrawing.drawWithNSLayout()
    
                    else:
                        FrameworkTextDrawing.drawWithCustomNSLayout()
                else:
                    AppDrawing.DispatchDrawing(context, _drawingCommand)
    
            else:
                mediaRect = Quartz.CGPDFDocumentGetMediaBox(_pdfDocument, 1)
                mediaRect.origin.x = mediaRect.origin.y = 0
                Quartz.CGContextDrawPDFDocument(context, mediaRect, _pdfDocument, 1)
    
        @objc.IBAction
        def setDrawCommand_(self, sender):
            global _drawingCommand, _pdfDocument
    
            newCommand = sender.tag()
            if _drawingCommand != newCommand:
                _drawingCommand = newCommand
                # The view needs to be redisplayed since there is a new drawing command.
                self.setNeedsDisplay_(True)
    
                # Disable previous menu item.
                if self.currentMenuItem is not None:
                    self.currentMenuItem.setState_(Cocoa.NSOffState)
    
                # Update the current item.
                self.currentMenuItem = sender
    
                # Enable new menu item.
                self.currentMenuItem.setState_(Cocoa.NSOnState)
    
                # If we were showing a pasted document, let's get rid of it.
                if _pdfDocument:
                    _pdfDocument = None
    
        def currentPrintableCommand(self):
            # The best representation for printing or exporting
            # when the current command caches using a bitmap context
            # or a layer is to not do any caching.
            if _drawingCommand in (
                UIHandling.kHICommandDrawOffScreenImage,
                UIHandling.kHICommandDrawWithLayer,
            ):
                return UIHandling.kHICommandDrawNoOffScreenImage
    
            return _drawingCommand
    
        def print_(self, sender):
            global _drawingCommand
    
            savedDrawingCommand = _drawingCommand
            # Set the drawing command to be one that is printable.
            _drawingCommand = self.currentPrintableCommand()
            # Do the printing operation on the view.
            Cocoa.NSPrintOperation.printOperationWithView_(self).runOperation()
            # Restore that before the printing operation.
            _drawingCommand = savedDrawingCommand
    
        def acceptsFirstResponder(self):
            return True
    
        @objc.IBAction
        def copy_(self, sender):
            FrameworkUtilities.addPDFDataToPasteBoard(_drawingCommand)
    
        @objc.IBAction
        def paste_(self, sender):
            global _pdfDocument
    
            newPDFDocument = PDFHandling.createNewPDFRefFromPasteBoard()
            if newPDFDocument is not None:
                _pdfDocument = newPDFDocument
                # The view needs to be redisplayed since there is
                # a new PDF document.
                self.setNeedsDisplay_(True)
    
        # Return the number of pages available for printing. For this
        # application it is always 1.
        def knowsPageRange_(self, aRange):
            return True, Cocoa.NSRange(1, 1)
    
        # Return the drawing rectangle for a particular page number.
        # For this application it is always the page width and height.
        def rectForPage_(self, page):
            pi = Cocoa.NSPrintOperation.currentOperation().printInfo()
    
            # Calculate the page height in points.
            paperSize = pi.paperSize()
            return Cocoa.NSMakeRect(0, 0, paperSize.width, paperSize.height)
    
        def validateMenuItem_(self, menuItem):
            if menuItem.tag() == _drawingCommand:
                self.currentMenuItem = menuItem
                menuItem.setState_(True)
            else:
                menuItem.setState_(False)
    
            return True

.. rst-class:: tabbertab

PDFHandling.py
..............

.. sourcecode:: python

    import AppDrawing
    import Cocoa
    import DataProvidersAndConsumers
    import FrameworkUtilities
    import Quartz
    
    
    def createNewPDFRefFromPasteBoard():
        # Create a reference to the PDF data on the pasteboard.
        # The implementation of myCreatePDFDataFromPasteBoard depends
        # on the application framework you are using for your application.
        #
        # myCreatePDFDataFromPasteBoard creates a reference that is owned
        # by the calling application.
        pasteBoardData = FrameworkUtilities.myCreatePDFDataFromPasteBoard()
    
        if pasteBoardData is None:
            print("There is no PDF data on pasteboard!")
            return None
    
        # Create a data provider from the pasteboard data.
        dataProvider = Quartz.CGDataProviderCreateWithCFData(pasteBoardData)
        # Release the pasteboard data since the data provider retains
        # it and this code owns a reference but no longer requires it.
        del pasteBoardData
    
        if dataProvider is None:
            print("Couldn't create data provider.")
            return None
    
        pasteBoardPDFDocument = Quartz.CGPDFDocumentCreateWithProvider(dataProvider)
        # Release the data provider now that the code is done with it.
        del dataProvider
    
        if pasteBoardPDFDocument is None:
            print("Couldn't create PDF document from pasteboard data provider.")
            return None
        return pasteBoardPDFDocument
    
    
    _pdfDoc = None
    
    
    def getPasteBoardPDFDoc(reset):
        global _pdfDoc
        if reset:
            # Release any existing document.
            _pdfDoc = createNewPDFRefFromPasteBoard()
        else:
            # If there isn't already one, create it fresh.
            if _pdfDoc is None:
                _pdfDoc = createNewPDFRefFromPasteBoard()
        return _pdfDoc
    
    
    def drawPasteBoardPDF(context):
        pdfDoc = getPasteBoardPDFDoc(False)  # Obtain the existing one.
        if pdfDoc is None:
            print("Quartz couldn't create CGPDFDocumentRef from pasteboard.")
            return
    
        # The media box is the bounding box of the PDF document.
        pdfRect = Quartz.CGPDFDocumentGetMediaBox(pdfDoc, 1)
        # page 1
        # Make the destination rect origin at the Quartz origin.
        pdfRect.origin.x = pdfRect.origin.y = 0.0
        Quartz.CGContextDrawPDFDocument(context, pdfRect, pdfDoc, 1)
        # page 1
    
    
    def cfDataCreatePDFDocumentFromCommand(command):
        # Media rect for the drawing. In a real application this
        # should be the bounding rectangle of the graphics
        # that will be the PDF content.
        mediaRect = Quartz.CGRectMake(0, 0, 612, 792)
    
        # Create a dictionary to hold the optional information describing the PDF data.
        info_dict = {}
    
        # Add the creator and title information to the PDF content.
        info_dict[Quartz.kCGPDFContextTitle] = "Pasted From Sample Quartz Application"
        info_dict[Quartz.kCGPDFContextCreator] = "Sample Quartz Application"
    
        # Create a mutable CFData object with unlimited capacity.
        data = Cocoa.CFDataCreateMutable(None, 0)
        if data is None:
            print("Couldn't make CFData!")
            return None
    
        # Create the data consumer to capture the PDF data.
        consumer = DataProvidersAndConsumers.myCGDataConsumerCreateWithCFData(data)
        if consumer is None:
            print("Couldn't create data consumer!")
            return None
    
        pdfContext, mediaRect = Quartz.CGPDFContextCreate(consumer, None, info_dict)
        del consumer
        del info_dict
    
        if pdfContext is None:
            print("Couldn't create pdf context!")
            return None
    
        mediaRect = Quartz.CGContextBeginPage(pdfContext)
        if 1:
            Quartz.CGContextSaveGState(pdfContext)
            if 1:
                Quartz.CGContextClipToRect(pdfContext, mediaRect)
                AppDrawing.DispatchDrawing(pdfContext, command)
            Quartz.CGContextRestoreGState(pdfContext)
        Quartz.CGContextEndPage(pdfContext)
    
        return data
    
    
    def MakePDFDocument(url, exportInfo):
        # Use this as the media box for the document.
        # In a real application this should be the bounding
        # rectangle of the graphics that will be the PDF content.
        mediaRect = Quartz.CGRectMake(0, 0, 612, 792)
    
        info = {
            # Add the title information for this document.
            Quartz.kCGPDFContextTitle: "BasicDrawing Sample Graphics",
            # Add the author information for this document. This is typically
            # the user creating the document.
            Quartz.kCGPDFContextAuthor: "David Gelphman and Bunny Laden",
            # The creator is the application creating the document.
            Quartz.kCGPDFContextCreator: "BasicDrawing Application",
        }
    
        if 0:
            # Before using the kCGPDFContextCropBox key, check to ensure that it
            # is available.
            if hasattr(Quartz, "kCFPDFContextCropBox"):
                # Prepare the crop box entry. Use this rectangle as the crop box for
                # this example.
    
                # XXX:fixme: need to encode as CFData!!!
                info[Quartz.kCGPDFContextCropBox] = Quartz.CGRectMake(100, 100, 200, 200)
    
        if url is not None:
            pdfContext = Quartz.CGPDFContextCreateWithURL(url, mediaRect, info)
            if pdfContext is not None:
                Quartz.CGContextBeginPage(pdfContext, mediaRect)
                if 1:
                    Quartz.CGContextSaveGState(pdfContext)
                    if 1:
                        Quartz.CGContextClipToRect(pdfContext, mediaRect)
                        AppDrawing.DispatchDrawing(pdfContext, exportInfo.command)
                    Quartz.CGContextRestoreGState(pdfContext)
                Quartz.CGContextEndPage(pdfContext)
                del pdfContext
            else:
                print("Can't create PDF document!")

.. rst-class:: tabbertab

PathDrawing.py
..............

.. sourcecode:: python

    import math
    
    import Quartz
    
    
    def doEgg(context):
        p0 = Quartz.CGPoint(0, 0)
        p1 = Quartz.CGPoint(0, 200)
        c1 = Quartz.CGPoint(140, 5)
        c2 = Quartz.CGPoint(80, 198)
    
        Quartz.CGContextTranslateCTM(context, 100, 5)
        Quartz.CGContextBeginPath(context)
    
        Quartz.CGContextMoveToPoint(context, p0.x, p0.y)
        # Create the Bezier path segment for the right side of the egg.
        Quartz.CGContextAddCurveToPoint(context, c1.x, c1.y, c2.x, c2.y, p1.x, p1.y)
        # Create the Bezier path segment for the left side of the egg.
        Quartz.CGContextAddCurveToPoint(context, -c2.x, c2.y, -c1.x, c1.y, p0.x, p0.y)
        Quartz.CGContextClosePath(context)
        Quartz.CGContextSetLineWidth(context, 2)
        Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke)
    
    
    def addRoundedRectToPath(context, rect, ovalWidth, ovalHeight):
        # If either ovalWidth or ovalHeight is 0, draw a regular rectangle.
        if ovalWidth == 0 or ovalHeight == 0:
            Quartz.CGContextAddRect(context, rect)
        else:
            Quartz.CGContextSaveGState(context)
            if 1:
                # Translate to lower-left corner of rectangle.
                Quartz.CGContextTranslateCTM(
                    context, Quartz.CGRectGetMinX(rect), Quartz.CGRectGetMinY(rect)
                )
                # Scale by the oval width and height so that
                # each rounded corner is 0.5 units in radius.
                Quartz.CGContextScaleCTM(context, ovalWidth, ovalHeight)
                # Unscale the rectangle width by the amount of the X scaling.
                fw = Quartz.CGRectGetWidth(rect) / ovalWidth
                # Unscale the rectangle height by the amount of the Y scaling.
                fh = Quartz.CGRectGetHeight(rect) / ovalHeight
                # Start at the right edge of the rect, at the midpoint in Y.
                Quartz.CGContextMoveToPoint(context, fw, fh / 2)
                # Segment 1
                Quartz.CGContextAddArcToPoint(context, fw, fh, fw / 2, fh, 0.5)
                # Segment 2
                Quartz.CGContextAddArcToPoint(context, 0, fh, 0, fh / 2, 0.5)
                # Segment 3
                Quartz.CGContextAddArcToPoint(context, 0, 0, fw / 2, 0, 0.5)
                # Segment 4
                Quartz.CGContextAddArcToPoint(context, fw, 0, fw, fh / 2, 0.5)
                # Closing the path adds the last segment.
                Quartz.CGContextClosePath(context)
            Quartz.CGContextRestoreGState(context)
    
    
    def doRoundedRects(context):
        rect = Quartz.CGRectMake(10, 10, 210, 150)
        ovalWidth = 100
        ovalHeight = 100
        Quartz.CGContextSetLineWidth(context, 2.0)
        Quartz.CGContextBeginPath(context)
        addRoundedRectToPath(context, rect, ovalWidth, ovalHeight)
        Quartz.CGContextSetRGBStrokeColor(context, 1, 0, 0, 1)
        Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke)
    
    
    def doStrokeWithCTM(context):
        Quartz.CGContextTranslateCTM(context, 150.0, 180.0)
        Quartz.CGContextSetLineWidth(context, 10)
        # Draw ellipse 1 with a uniform stroke.
        Quartz.CGContextSaveGState(context)
        if 1:
            # Scale the CTM so the circular arc will be elliptical.
            Quartz.CGContextScaleCTM(context, 2, 1)
            Quartz.CGContextBeginPath(context)
            # Create an arc that is a circle.
            Quartz.CGContextAddArc(context, 0.0, 0.0, 45.0, 0.0, 2 * math.pi, 0)
            # Restore the context parameters prior to stroking the path.
            # CGContextRestoreGState does not affect the path in the context.
        Quartz.CGContextRestoreGState(context)
        Quartz.CGContextStrokePath(context)
    
        # *** was 0, -120
        Quartz.CGContextTranslateCTM(context, 220.0, 0.0)
        # Draw ellipse 2 with non-uniform stroke.
        Quartz.CGContextSaveGState(context)
        if 1:
            # Scale the CTM so the circular arc will be elliptical.
            Quartz.CGContextScaleCTM(context, 2, 1)
            Quartz.CGContextBeginPath(context)
            # Create an arc that is a circle.
            Quartz.CGContextAddArc(context, 0.0, 0.0, 45.0, 0.0, 2 * math.pi, 0)
            # Stroke the path with the scaled coordinate system in effect.
            Quartz.CGContextStrokePath(context)
        Quartz.CGContextRestoreGState(context)
    
    
    def doRotatedEllipsesWithCGPath(context):
        totreps = 144
        tint = 1.0
        tintIncrement = 1.0 / totreps
    
        # Create a new transform consisting of a 45 degree rotation.
        theTransform = Quartz.CGAffineTransformMakeRotation(math.pi / 4)
        # Apply a scaling transformation to the transform just created.
        theTransform = Quartz.CGAffineTransformScale(theTransform, 1, 2)
        # Create a mutable CGPath object.
        path = Quartz.CGPathCreateMutable()
        if path is None:
            print("Couldn't create path!")
            return
    
        # Add a circular arc to the CGPath object, transformed
        # by an affine transform.
        Quartz.CGPathAddArc(path, theTransform, 0.0, 0.0, 45.0, 0.0, 2 * math.pi, False)
        # Close the CGPath object.
        Quartz.CGPathCloseSubpath(path)
    
        # Place the first ellipse at a good location.
        Quartz.CGContextTranslateCTM(context, 100, 100)
        for _ in range(totreps):
            Quartz.CGContextBeginPath(context)
            # Add the CGPath object to the current path in the context.
            Quartz.CGContextAddPath(context, path)
    
            # Set the fill color for this instance of the ellipse.
            Quartz.CGContextSetRGBFillColor(context, tint, 0.0, 0.0, 1.0)
            # Filling the path implicitly closes it.
            Quartz.CGContextFillPath(context)
            # Compute the next tint color.
            tint -= tintIncrement
            # Move over for the next ellipse.
            Quartz.CGContextTranslateCTM(context, 1, 0.0)
    
    
    def alignPointToUserSpace(context, p):
        # Compute the coordinates of the point in device space.
        p = Quartz.CGContextConvertPointToDeviceSpace(context, p)
        # Ensure that coordinates are at exactly the corner
        # of a device pixel.
        p.x = math.floor(p.x)
        p.y = math.floor(p.y)
        # Convert the device aligned coordinate back to user space.
        return Quartz.CGContextConvertPointToUserSpace(context, p)
    
    
    def alignSizeToUserSpace(context, s):
        # Compute the size in device space.
        s = Quartz.CGContextConvertSizeToDeviceSpace(context, s)
        # Ensure that size is an integer multiple of device pixels.
        s.width = math.floor(s.width)
        s.height = math.floor(s.height)
        # Convert back to user space.
        return Quartz.CGContextConvertSizeToUserSpace(context, s)
    
    
    def alignRectToUserSpace(context, r):
        # Compute the coordinates of the rectangle in device space.
        r = Quartz.CGContextConvertRectToDeviceSpace(context, r)
        # Ensure that the x and y coordinates are at a pixel corner.
        r.origin.x = math.floor(r.origin.x)
        r.origin.y = math.floor(r.origin.y)
        # Ensure that the width and height are an integer number of
        # device pixels. Note that this produces a width and height
        # that is less than or equal to the original width. Another
        # approach is to use ceil to ensure that the new rectangle
        # encloses the original one.
        r.size.width = math.floor(r.size.width)
        r.size.height = math.floor(r.size.height)
    
        # Convert back to user space.
        return Quartz.CGContextConvertRectToUserSpace(context, r)
    
    
    def doPixelAlignedFillAndStroke(context):
        p1 = Quartz.CGPointMake(16.7, 17.8)
        p2 = Quartz.CGPointMake(116.7, 17.8)
        r = Quartz.CGRectMake(16.7, 20.8, 100.6, 100.6)
    
        Quartz.CGContextSetLineWidth(context, 2)
        Quartz.CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0)
        Quartz.CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0)
    
        # Unaligned drawing.
        Quartz.CGContextBeginPath(context)
        Quartz.CGContextMoveToPoint(context, p1.x, p1.y)
        Quartz.CGContextAddLineToPoint(context, p2.x, p2.y)
        Quartz.CGContextStrokePath(context)
        Quartz.CGContextFillRect(context, r)
    
        # Translate to the right before drawing along
        # aligned coordinates.
        Quartz.CGContextTranslateCTM(context, 106, 0)
    
        # Aligned drawing.
    
        # Compute the length of the line in user space.
        s = Quartz.CGSizeMake(p2.x - p1.x, p2.y - p1.y)
    
        Quartz.CGContextBeginPath(context)
        # Align the starting point to a device
        # pixel boundary.
        p1 = alignPointToUserSpace(context, p1)
        # Establish the starting point of the line.
        Quartz.CGContextMoveToPoint(context, p1.x, p1.y)
        # Compute the line length as an integer
        # number of device pixels.
        s = alignSizeToUserSpace(context, s)
        Quartz.CGContextAddLineToPoint(context, p1.x + s.width, p1.y + s.height)
        Quartz.CGContextStrokePath(context)
        # Compute a rect that is aligned to device
        # space with a width that is an integer
        # number of device pixels.
        r = alignRectToUserSpace(context, r)
        Quartz.CGContextFillRect(context, r)

.. rst-class:: tabbertab

PatternDrawing.py
.................

.. sourcecode:: python

    import Quartz
    import Utilities
    
    
    def scalePatternPhase(phase):
        # Adjust the pattern phase if scaling to export as bits. This is equivalent to scaling base
        # space by the scaling factor.
        patternScaling = Utilities.getScalingFactor()
        if patternScaling != 1.0:
            phase = Quartz.CGSizeApplyAffineTransform(
                phase, Quartz.CGAffineTransformMakeScale(patternScaling, patternScaling)
            )
    
        return phase
    
    
    def scalePatternMatrix(patternTransform):
        # Scale the pattern by the scaling factor when exporting to bits. This is equivalent to
        # scaling base space by the scaling factor.
        patternScaling = Utilities.getScalingFactor()
        if patternScaling != 1.0:
            patternTransform = Quartz.CGAffineTransformConcat(
                patternTransform,
                Quartz.CGAffineTransformMakeScale(patternScaling, patternScaling),
            )
    
        return patternTransform
    
    
    def myDrawRedBlackCheckerBoardPattern(info, patternCellContext):
        # This pattern proc draws a red and a black rectangle
        # patch representing the minimum cell needed to paint a
        # checkerboard with that pattern.
        #
        # Each 'cell' of the checkerboard is 2 units on a side.
        #
        # This code uses Quartz.CGColorRefs which are available in Panther
        # and later only. Patterns are available in all versions of
        # macOS but this code uses Quartz.CGColorRefs for convenience
        # and efficiency.
    
        # Paint a black checkerboard box.
        Quartz.CGContextSetFillColorWithColor(
            patternCellContext, Utilities.getRGBOpaqueBlackColor()
        )
        # This is a 1x1 unit rect whose origin is at 0,0 in pattern space.
        Quartz.CGContextFillRect(patternCellContext, Quartz.CGRectMake(0.0, 0.0, 1.0, 1.0))
        # This is a 1x1 unit rect whose origin is at 1,1 in pattern space.
        Quartz.CGContextFillRect(patternCellContext, Quartz.CGRectMake(1.0, 1.0, 1.0, 1.0))
    
        # Paint a red checkerboard box.
        Quartz.CGContextSetFillColorWithColor(
            patternCellContext, Utilities.getRGBOpaqueRedColor()
        )
        # This is a 1x1 unit rect whose origin is at 1,0 in pattern space,
        # that is, immediately to the right of first black checkerboard box.
        Quartz.CGContextFillRect(patternCellContext, Quartz.CGRectMake(1.0, 0.0, 1.0, 1.0))
        # This is a 1x1 unit rect whose origin is at 0,1 in pattern space,
        # that is, immediately above the first black checkerboard box.
        Quartz.CGContextFillRect(patternCellContext, Quartz.CGRectMake(0.0, 1.0, 1.0, 1.0))
    
    
    def createRedBlackCheckerBoardPattern(patternTransform):
        pattern = Quartz.CGPatternCreate(
            None,
            # The pattern cell origin is at (0,0) with a
            # width of 2 units and a height of 2 units.
            Quartz.CGRectMake(0, 0, 2, 2),
            # Use the pattern transform supplied to this routine.
            scalePatternMatrix(patternTransform),
            # In pattern space the xStep is 2 units to the next cell in x
            # and the yStep is 2 units to the next row of cells in y.
            2,
            2,
            # This value is a good choice for this type of pattern and it
            # avoids seams between tiles.
            Quartz.kCGPatternTilingConstantSpacingMinimalDistortion,
            # This pattern has intrinsic color.
            True,
            myDrawRedBlackCheckerBoardPattern,
        )
        return pattern
    
    
    def doRedBlackCheckerboard(context):
        dash = [4]
        pattern = createRedBlackCheckerBoardPattern(
            Quartz.CGAffineTransformMakeScale(20, 20)
        )
        if pattern is None:
            print("Couldn't create pattern!")
            return
    
        # Create the pattern color space. Since the pattern
        # itself has intrinsic color, the 'baseColorSpace' parameter
        # to Quartz.CGColorSpaceCreatePattern must be None.
        patternColorSpace = Quartz.CGColorSpaceCreatePattern(None)
        Quartz.CGContextSetFillColorSpace(context, patternColorSpace)
    
        # The pattern has intrinsic color so the color components array
        # passed to CGContextSetFillPattern is just the alpha value used
        # to composite the pattern cell.
    
        # Paint the pattern with alpha = 1.
        color = [1.0]
    
        # Set the fill color to the checkerboard pattern.
        Quartz.CGContextSetFillPattern(context, pattern, color)
    
        # Fill a 100x100 unit rect at (20,20).
        Quartz.CGContextFillRect(context, Quartz.CGRectMake(20, 20, 100, 100))
    
        # Save the graphics state before changing the stroke color.
        Quartz.CGContextSaveGState(context)
        if 1:
            # Set the stroke color space and color to the pattern.
            Quartz.CGContextSetStrokeColorSpace(context, patternColorSpace)
            Quartz.CGContextSetStrokePattern(context, pattern, color)
    
            # Stroke an ellipse with the pattern.
            Quartz.CGContextSetLineWidth(context, 8)
            Quartz.CGContextBeginPath(context)
            Utilities.myCGContextAddEllipseInRect(
                context, Quartz.CGRectMake(120, 20, 50, 100)
            )
            Quartz.CGContextStrokePath(context)
    
        # Restore to the graphics state without the
        # pattern stroke color.
        Quartz.CGContextRestoreGState(context)
    
        # Now draw text.
        Quartz.CGContextSetTextMatrix(context, Quartz.CGAffineTransformIdentity)
        # Choose the font with the PostScript name "Times-Roman",
        # size 80 points, with the encoding MacRoman encoding.
        Quartz.CGContextSelectFont(context, b"Times-Roman", 80, Quartz.kCGEncodingMacRoman)
    
        # Using the fill text drawing mode.
        Quartz.CGContextSetTextDrawingMode(context, Quartz.kCGTextFill)
    
        # Draw text with the pattern.
        Quartz.CGContextShowTextAtPoint(context, 20, 120, b"Text", 4)
    
        # Rectangle 1, filled.
        Quartz.CGContextFillRect(context, Quartz.CGRectMake(200, 20, 90, 90))
    
        # Rectangle 2, filled and stroked with a dash.
        Quartz.CGContextSetLineWidth(context, 2)
        Quartz.CGContextSetLineDash(context, 0, dash, 1)
        Quartz.CGContextBeginPath(context)
        Quartz.CGContextAddRect(context, Quartz.CGRectMake(200, 70, 90, 90))
        Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
    
    
    def doPatternMatrix(context):
        basePatternMatrix = Quartz.CGAffineTransformMakeScale(20, 20)
        pattern = createRedBlackCheckerBoardPattern(basePatternMatrix)
        if pattern is None:
            print("Couldn't create pattern!")
            return
    
        # Create the pattern color space. Since the pattern
        # itself has intrinsic color, the 'baseColorSpace' parameter
        # to Quartz.CGColorSpaceCreatePattern must be None.
        patternColorSpace = Quartz.CGColorSpaceCreatePattern(None)
    
        Quartz.CGContextSetFillColorSpace(context, patternColorSpace)
        del patternColorSpace
    
        Quartz.CGContextTranslateCTM(context, 40, 40)
        Quartz.CGContextSetPatternPhase(context, scalePatternPhase(Quartz.CGSize(40, 40)))
    
        # The pattern has intrinsic color so the color components array
        # passed to Quartz.CGContextSetFillPattern is the alpha value used
        # to composite the pattern cell.
    
        # Paint the pattern first with alpha = 1.
        color = [1]
        Quartz.CGContextSetFillPattern(context, pattern, color)
    
        # Rectangle 1.
        Quartz.CGContextFillRect(context, Quartz.CGRectMake(0, 0, 100, 100))
    
        Quartz.CGContextSaveGState(context)
        if 1:
            # Rectangle 2.
            # Paint the pattern with 65% alpha.
            color = [0.65]
            Quartz.CGContextSetFillPattern(context, pattern, color)
            # Rotate 45 degrees about the point (150, 50).
            Quartz.CGContextTranslateCTM(context, 150.0, 50.0)
            Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(45.0))
            Quartz.CGContextTranslateCTM(context, -50.0, -50.0)
            # Rectangle 2. Patterns do not translate, scale or
            # rotate with the CTM. You can see that the pattern
            # tile of this filled rectangle is that of Rectangle
            # 1.
            Quartz.CGContextFillRect(context, Quartz.CGRectMake(0, 0, 100, 100))
            # Release the pattern.
            del pattern
        Quartz.CGContextRestoreGState(context)
    
        Quartz.CGContextSaveGState(context)
        if 1:
            # Rectangle 3. The pattern is rotated with the object.
            # Rotate 45 degrees about the point 250, 50.
            t = Quartz.CGAffineTransformMakeTranslation(250.0, 50.0)
            t = Quartz.CGAffineTransformRotate(t, Utilities.DEGREES_TO_RADIANS(45.0))
            # Translate back to -50, -50.
            t = Quartz.CGAffineTransformTranslate(t, -50.0, -50.0)
            Quartz.CGContextConcatCTM(context, t)
            # Make a new pattern that is equivalent to
            # the old pattern but transformed to current user
            # space. The order of transformations is crucial.
            # This ordering is equivalent to using the same pattern
            # matrix as before but transforming base space by t.
            patTransform = Quartz.CGAffineTransformConcat(basePatternMatrix, t)
            pattern = createRedBlackCheckerBoardPattern(patTransform)
            color = [1]
            Quartz.CGContextSetFillPattern(context, pattern, color)
            # Release the pattern.
            del pattern
            Quartz.CGContextFillRect(context, Quartz.CGRectMake(0, 0, 100, 100))
        Quartz.CGContextRestoreGState(context)
    
        Quartz.CGContextSaveGState(context)
        if 1:
            # Rectangle 4. The pattern is scaled with the object.
            # Translate and scale.
            t = Quartz.CGAffineTransformMakeTranslation(320, 0)
            t = Quartz.CGAffineTransformScale(t, 2, 2)
            Quartz.CGContextConcatCTM(context, t)
            # Make a new pattern that is equivalent to
            # the old pattern but transformed to current user
            # space. The order of transformations is crucial.
            # This ordering is equivalent to using the same pattern
            # matrix as before but transforming base space by t.
            patTransform = Quartz.CGAffineTransformConcat(basePatternMatrix, t)
            pattern = createRedBlackCheckerBoardPattern(patTransform)
            color = [1]
            Quartz.CGContextSetFillPattern(context, pattern, color)
            # Release the pattern.
            del pattern
            Quartz.CGContextFillRect(context, Quartz.CGRectMake(0, 0, 100, 100))
        Quartz.CGContextRestoreGState(context)
    
    
    def doPatternPhase(context):
        pattern = createRedBlackCheckerBoardPattern(
            Quartz.CGAffineTransformMakeScale(20, 20)
        )
        if pattern is None:
            print("Couldn't create pattern!")
            return
    
        # Create the pattern color space for a colored pattern.
        patternColorSpace = Quartz.CGColorSpaceCreatePattern(None)
        Quartz.CGContextSetFillColorSpace(context, patternColorSpace)
    
        # Paint the pattern with alpha = 1.
        color = (1,)
        Quartz.CGContextSetFillPattern(context, pattern, color)
    
        # Rectangle 1
        Quartz.CGContextFillRect(context, Quartz.CGRectMake(20, 150, 100, 100))
    
        # Rectangle 2
        Quartz.CGContextFillRect(context, Quartz.CGRectMake(130, 150, 100, 100))
    
        # Rectangle 3
        # Set the pattern phase so that the pattern origin
        # is at the lower-left of the shape.
        Quartz.CGContextSetPatternPhase(
            context, scalePatternPhase(Quartz.CGSizeMake(20, 20))
        )
        Quartz.CGContextFillRect(context, Quartz.CGRectMake(20, 20, 100, 100))
    
        # Rectangle 4
        # Set the pattern phase so that the pattern origin
        # is at the lower-left corner of the shape.
        Quartz.CGContextSetPatternPhase(
            context, scalePatternPhase(Quartz.CGSizeMake(130, 20))
        )
        Quartz.CGContextTranslateCTM(context, 130, 20)
        Quartz.CGContextFillRect(context, Quartz.CGRectMake(0, 0, 100, 100))
    
    
    def drawRotatedRect(c, p):
        r = Quartz.CGRectMake(0, 0, 1, 1)
        Quartz.CGContextSaveGState(c)
        if 1:
            Quartz.CGContextTranslateCTM(c, p.x, p.y)
            Quartz.CGContextRotateCTM(c, Utilities.DEGREES_TO_RADIANS(45))
            Quartz.CGContextTranslateCTM(c, -r.size.width / 2, -r.size.height / 2)
            Quartz.CGContextFillRect(c, r)
        Quartz.CGContextRestoreGState(c)
    
    
    def myStencilPatternProc(info, patternCellContext):
        drawRotatedRect(patternCellContext, Quartz.CGPointMake(1, 1))
        drawRotatedRect(patternCellContext, Quartz.CGPointMake(1.75, 1))
    
    
    def createStencilPattern(patternTransform):
        pattern = Quartz.CGPatternCreate(
            None,
            # The pattern cell origin is at (0,0) with a
            # width of 2.5 units and a height of 2 units. This
            # pattern cell has transparent areas since
            # the pattern proc only marks a portion of the cell.
            Quartz.CGRectMake(0, 0, 2.5, 2),
            # Use the pattern transform supplied to this routine.
            scalePatternMatrix(patternTransform),
            # Use the width and height of the pattern cell for
            # the xStep and yStep.
            2.5,
            2,
            # This value is a good choice for this type of pattern and it
            # avoids seams between tiles.
            Quartz.kCGPatternTilingConstantSpacingMinimalDistortion,
            # This pattern does not have intrinsic color.
            False,  # Must be False for a stencil pattern.
            myStencilPatternProc,
        )
        return pattern
    
    
    def doStencilPattern(context):
        pattern = createStencilPattern(Quartz.CGAffineTransformMakeScale(20, 20))
        if pattern is None:
            print("Couldn't create pattern!")
            return
    
        # Create the pattern color space. This pattern is a stencil
        # pattern so when the code sets the pattern it also sets the
        # color it will paint the pattern with. In order to
        # set the pattern color space in this case we also have
        # to say what underlying color space should be used when
        # the pattern proc is called.
        baseColorSpace = Utilities.getTheCalibratedRGBColorSpace()
        patternColorSpace = Quartz.CGColorSpaceCreatePattern(baseColorSpace)
    
        Quartz.CGContextSetFillColorSpace(context, patternColorSpace)
        # This code is finished with the pattern color space and can release
        # it because Quartz retains it while it is the current color space.
        del patternColorSpace
    
        # The pattern has no intrinsic color so the color components array
        # passed to CGContextSetFillPattern contains the colors to paint
        # the pattern with in the baseColorSpace. In the case here,
        # first paint the pattern with opaque blue.
        color = (0.11, 0.208, 0.451, 1.0)
        Quartz.CGContextSetFillPattern(context, pattern, color)
    
        # Rectangle 1.
        Quartz.CGContextSetPatternPhase(
            context, scalePatternPhase(Quartz.CGSizeMake(20, 160))
        )
        Quartz.CGContextBeginPath(context)
        Quartz.CGContextAddRect(context, Quartz.CGRectMake(20, 160, 105, 80))
        Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
    
        # Rectangle 2.
        # Set the pattern color so the stencil pattern
        # is painted in a yellow shade.
        color = (1.0, 0.816, 0.0, 1.0)
        Quartz.CGContextSetFillPattern(context, pattern, color)
        # Set the pattern phase to the origin of the next object.
        Quartz.CGContextSetPatternPhase(
            context, scalePatternPhase(Quartz.CGSizeMake(140, 160))
        )
        Quartz.CGContextBeginPath(context)
        Quartz.CGContextAddRect(context, Quartz.CGRectMake(140, 160, 105, 80))
        Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
    
        Quartz.CGContextSaveGState(context)
        if 1:
            Quartz.CGContextSetFillColorWithColor(
                context, Utilities.getRGBOpaqueBlueColor()
            )
            # Fill color is now blue. Paint two blue rectangles
            # that will be underneath the drawing which follows.
            Quartz.CGContextFillRect(context, Quartz.CGRectMake(20, 40, 105, 80))
            Quartz.CGContextFillRect(context, Quartz.CGRectMake(140, 40, 105, 80))
        Quartz.CGContextRestoreGState(context)
    
        # The fill color is again the stencil pattern with
        # the underlying fill color an opaque yellow.
    
        # Rectangle 3.
        # This paints over the blue rect just painted at 20,40
        # and the blue underneath is visible where the pattern has
        # transparent areas.
        Quartz.CGContextSetPatternPhase(
            context, scalePatternPhase(Quartz.CGSizeMake(20, 40))
        )
        Quartz.CGContextFillRect(context, Quartz.CGRectMake(20, 40, 105, 80))
    
        # Rectangle 4.
        # Change the alpha value of the underlying color used
        # to paint the stencil pattern.
        color = list(color)
        color[3] = 0.75
        Quartz.CGContextSetFillPattern(context, pattern, color)
        Quartz.CGContextSetPatternPhase(
            context, scalePatternPhase(Quartz.CGSizeMake(140, 40))
        )
        Quartz.CGContextFillRect(context, Quartz.CGRectMake(140, 40, 105, 80))
    
    
    class MyPDFPatternInfo:
        rect = None
        pdfDoc = None
    
    
    def myDrawPDFPattern(info, patternCellContext):
        # This pattern proc draws the first page of a PDF document to
        # a destination rect.
        Quartz.CGContextSaveGState(patternCellContext)
        Quartz.CGContextClipToRect(patternCellContext, info.rect)
        Quartz.CGContextDrawPDFDocument(patternCellContext, info.rect, info.pdfDoc, 1)
        Quartz.CGContextRestoreGState(patternCellContext)
    
    
    # Versions of Tiger prior to 10.4.3 have a bug such that use of an xStep that
    # doesn't match the width of pattern bounding box or a yStep that doesn't match the
    # height of the pattern bounding box produces incorrect results when drawn
    # to a bit-based context. Setting TIGERSTEPWORKAROUND works around this bug.
    
    
    TIGERSTEPWORKAROUND = 1
    SCALEPATTERN = 1
    OPTIMIZEDPERF = 0
    
    
    def createPDFPatternPattern(additionalTransformP, url):
        patternInfoP = MyPDFPatternInfo()
    
        patternInfoP.pdfDoc = Quartz.CGPDFDocumentCreateWithURL(url)
        if patternInfoP.pdfDoc is None:
            print("Couldn't create PDF document reference!")
            return
    
        patternInfoP.rect = Quartz.CGPDFDocumentGetMediaBox(patternInfoP.pdfDoc, 1)
        # Set the origin of the media rect for the PDF document to (0,0).
        patternInfoP.rect.origin = Quartz.CGPointZero
    
        if additionalTransformP is not None:
            patternTransform = additionalTransformP
        else:
            patternTransform = Quartz.CGAffineTransformIdentity
    
        # To emulate the example from the bitmap context drawing chapter,
        # the tile offset in each dimension is the tile size in that
        # dimension, plus 6 units.
        if SCALEPATTERN:
            tileOffsetX = 6.0 + patternInfoP.rect.size.width
            tileOffsetY = 6.0 + patternInfoP.rect.size.height
        else:
            tileOffsetX = 2.0 + patternInfoP.rect.size.width
            tileOffsetY = 2.0 + patternInfoP.rect.size.height
    
        # Tiger versions 10.4.0 - 10.4.2 have a bug such that the bounds
        # width and height is incorrectly used as the xstep,ystep.
        # To workaround this bug, we can make the bounds rect incorporate
        # the xstep,ystep since xstep,ystep are larger than the bounds.
        if OPTIMIZEDPERF or TIGERSTEPWORKAROUND:
            patternRect = Quartz.CGRectMake(0, 0, tileOffsetX, tileOffsetY)
        else:
            patternRect = patternInfoP.rect
    
        if OPTIMIZEDPERF:
            # Produces best performance if bbox == xstep/ystep
            spacing = Quartz.kCGPatternTilingConstantSpacing
        else:
            spacing = Quartz.kCGPatternTilingConstantSpacingMinimalDistortion
    
        pattern = Quartz.CGPatternCreate(
            patternInfoP,
            # The pattern cell size is the size
            # of the media rect of the PDF document.
            patternRect,
            scalePatternMatrix(patternTransform),
            tileOffsetX,
            tileOffsetY,
            # This value is a good choice for this type of pattern and
            #  it avoids seams between tiles.
            spacing,
            # This pattern has intrinsic color.
            True,
            myDrawPDFPattern,
        )
        # If the pattern can't be created then release the
        # pattern resources and info parameter.
        if pattern is None:
            patternInfoP = None
    
        return pattern
    
    
    def drawWithPDFPattern(context, url):
        if SCALEPATTERN:
            patternMatrix = Quartz.CGAffineTransformMakeScale(1.0 / 3, 1.0 / 3)
        else:
            patternMatrix = Quartz.CGAffineTransformMakeScale(1, 1)
    
        # Scale the PDF pattern down to 1/3 its original size.
        pdfPattern = createPDFPatternPattern(patternMatrix, url)
        if pdfPattern is None:
            print("Couldn't create pattern!")
            return
    
        # Create the pattern color space. Since the pattern
        # itself has intrinsic color, the 'baseColorSpace' parameter
        # to CGColorSpaceCreatePattern must be None.
        patternColorSpace = Quartz.CGColorSpaceCreatePattern(None)
        Quartz.CGContextSetFillColorSpace(context, patternColorSpace)
        # Quartz retains the color space so this code
        # can now release it since it no longer needs it.
        del patternColorSpace
    
        # Paint the pattern with an alpha of 1.
        color = (1,)
        Quartz.CGContextSetFillPattern(context, pdfPattern, color)
        # Quartz retains the pattern so this code
        # can now release it since it no longer needs it.
        del pdfPattern
    
        # Fill a US Letter size rect with the pattern.
        Quartz.CGContextFillRect(context, Quartz.CGRectMake(0, 0, 612, 792))

.. rst-class:: tabbertab

QuartzTextDrawing.py
....................

.. sourcecode:: python

    import Quartz
    import Utilities
    
    
    def drawQuartzRomanText(context):
        text = b"Quartz"
        textlen = len(text)
        fontSize = 60
    
        opaqueBlack = [0.0, 0.0, 0.0, 1.0]
        opaqueRed = [0.663, 0.0, 0.031, 1.0]
    
        # Set the fill color space. This sets the
        # fill painting color to opaque black.
        Quartz.CGContextSetFillColorSpace(
            context, Utilities.getTheCalibratedRGBColorSpace()
        )
    
        # The Cocoa framework calls the draw method with an undefined
        # value of the text matrix. It's best to set it to what is needed by
        # this code: the identity transform.
        Quartz.CGContextSetTextMatrix(context, Quartz.CGAffineTransformIdentity)
    
        # Set the font with the PostScript name "Times-Roman", at
        # fontSize points, with the MacRoman encoding.
        Quartz.CGContextSelectFont(
            context, b"Times-Roman", fontSize, Quartz.kCGEncodingMacRoman
        )
    
        # The default text drawing mode is fill. Draw the text at (70, 400).
        Quartz.CGContextShowTextAtPoint(context, 70, 400, text, textlen)
    
        # Set the fill color to red.
        Quartz.CGContextSetFillColor(context, opaqueRed)
    
        # Draw the next piece of text where the previous one left off.
        Quartz.CGContextShowText(context, text, textlen)
    
        for _ in range(3):
            # Get the current text pen position.
            p = Quartz.CGContextGetTextPosition(context)
            # Translate to the current text pen position.
            Quartz.CGContextTranslateCTM(context, p.x, p.y)
    
            # Rotate clockwise by 90 degrees for the next
            # piece of text.
            Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(-90))
            # Draw the next piece of text in blac at the origin.
            Quartz.CGContextSetFillColor(context, opaqueBlack)
            Quartz.CGContextShowTextAtPoint(context, 0, 0, text, textlen)
            # Draw the next piece of text where the previous piece
            # left off and paint it with red.
            Quartz.CGContextSetFillColor(context, opaqueRed)
            Quartz.CGContextShowText(context, text, textlen)
    
    
    def myCGContextStrokeLineSegments(context, s, count):
        # CGContextStrokeLineSegments is available only on Tiger and later
        # so if it isn't available, use an emulation of
        # CGContextStrokeLineSegments. It is better to use the
        # built-in CGContextStrokeLineSegments since it has significant
        # performance optimizations on some hardware.
        if hasattr(Quartz, "CGContextStrokeLineSegments"):
            Quartz.CGContextStrokeLineSegments(context, s, count)
        else:
            Quartz.CGContextBeginPath(context)
            for k in range(0, count, 2):
                Quartz.CGContextMoveToPoint(context, s[k].x, s[k].y)
                Quartz.CGContextAddLineToPoint(context, s[k + 1].x, s[k + 1].y)
            Quartz.CGContextStrokePath(context)
    
    
    _gridLines = []
    
    
    def drawGridLines(context):
        numlines = 60
    
        if not _gridLines:
            stepsize = 4.0
            val = 0
            for _ in range(0, 2 * numlines, 2):
                _gridLines.append(Quartz.CGPointMake(val, -60))
                _gridLines.append(Quartz.CGPointMake(val, 200))
                val += stepsize
    
            val = -20
            for _ in range(2 * numlines, 4 * numlines, 2):
                _gridLines.append(Quartz.CGPointMake(0, val))
                _gridLines.append(Quartz.CGPointMake(400, val))
                val += stepsize
    
        myCGContextStrokeLineSegments(context, _gridLines, len(_gridLines))
    
    
    def drawQuartzTextWithTextModes(context):
        fillText = b"Fill "
        strokeText = b"Stroke "
        fillAndStrokeText = b"FillStroke "
        invisibleText = b"Invisible "
        clipText = b"ClipText "
        fillStrokeClipText = b"FillStrokeClip "
        fontSize = 40.0
        extraLeading = 5.0
        dash = (1, 1)
        opaqueRed = (1.0, 0.0, 0.0, 1.0)
    
        # Set the fill and stroke color space. This sets the
        # fill and stroke painting color to opaque black.
        Quartz.CGContextSetFillColorSpace(
            context, Utilities.getTheCalibratedRGBColorSpace()
        )
        Quartz.CGContextSetStrokeColorSpace(
            context, Utilities.getTheCalibratedRGBColorSpace()
        )
    
        # The Cocoa framework calls the draw method with an undefined
        # value of the text matrix. It's best to set it to what is needed by
        # this code: the identity transform.
        Quartz.CGContextSetTextMatrix(context, Quartz.CGAffineTransformIdentity)
    
        # Set the font with the PostScript name "Times-Roman", at
        # fontSize points, with the MacRoman encoding.
        Quartz.CGContextSelectFont(
            context, b"Times-Roman", fontSize, Quartz.kCGEncodingMacRoman
        )
    
        # ----  Text Line 1 ----
    
        # Default text drawing mode is fill. Draw the text at (10, 400).
        Quartz.CGContextShowTextAtPoint(context, 10, 400, fillText, len(fillText))
    
        # Set the fill color to red.
        Quartz.CGContextSetFillColor(context, opaqueRed)
    
        Quartz.CGContextSetTextPosition(context, 180, 400)
        Quartz.CGContextShowText(context, fillText, len(fillText))
    
        # Translate down for the next line of text.
        Quartz.CGContextTranslateCTM(context, 0, -(fontSize + extraLeading))
    
        # ----  Text Line 2 ----
    
        # Now stroke the text by setting the text drawing mode
        # to kCGTextStroke. When stroking text, Quartz uses the stroke
        # color in the graphics state.
        Quartz.CGContextSetTextDrawingMode(context, Quartz.kCGTextStroke)
        Quartz.CGContextShowTextAtPoint(context, 10, 400, strokeText, len(strokeText))
    
        # When stroking text, the line width and other gstate parameters
        # that affect stroking affect text stroking as well.
        Quartz.CGContextSetLineWidth(context, 2)
        Quartz.CGContextSetLineDash(context, 0, dash, 2)
    
        Quartz.CGContextSetTextPosition(context, 180, 400)
        Quartz.CGContextShowText(context, strokeText, len(strokeText))
    
        # Reset the line dash and line width to their defaults.
        Quartz.CGContextSetLineDash(context, 0, None, 0)
        Quartz.CGContextSetLineWidth(context, 1)
    
        # Translate down for the next line of text.
        Quartz.CGContextTranslateCTM(context, 0, -(fontSize + extraLeading))
    
        # ----  Text Line 3 ----
    
        # Set the text drawing mode so that text is both filled and
        # stroked. This produces text that is filled with the fill
        # color and stroked with the stroke color.
        Quartz.CGContextSetTextDrawingMode(context, Quartz.kCGTextFillStroke)
        Quartz.CGContextShowTextAtPoint(
            context, 10, 400, fillAndStrokeText, len(fillAndStrokeText)
        )
    
        # Now draw again with a thicker stroke width.
        Quartz.CGContextSetLineWidth(context, 2)
        Quartz.CGContextSetTextPosition(context, 180, 400)
        Quartz.CGContextShowText(context, fillAndStrokeText, len(fillAndStrokeText))
    
        Quartz.CGContextSetLineWidth(context, 1)
        Quartz.CGContextTranslateCTM(context, 0, -(fontSize + extraLeading))
    
        # ----  Text Line 4 ----
    
        # Set the text drawing mode to invisible so that the next piece of
        # text does not appear. Quartz updates the text position as
        # if it had been drawn.
        Quartz.CGContextSetTextDrawingMode(context, Quartz.kCGTextInvisible)
        Quartz.CGContextShowTextAtPoint(context, 10, 400, invisibleText, len(invisibleText))
    
        Quartz.CGContextSetTextDrawingMode(context, Quartz.kCGTextFill)
    
        Quartz.CGContextSetTextPosition(context, 180, 400)
        Quartz.CGContextShowText(context, fillText, len(fillText))
    
        Quartz.CGContextTranslateCTM(context, 0, -(fontSize + extraLeading))
    
        # ----  Text Line 5 ----
        Quartz.CGContextSaveGState(context)
        if 1:
            # Use the text as a clipping path.
            Quartz.CGContextSetTextDrawingMode(context, Quartz.kCGTextClip)
            Quartz.CGContextShowTextAtPoint(context, 10, 400, clipText, len(clipText))
    
            # Position and draw a grid of lines.
            Quartz.CGContextTranslateCTM(context, 10, 400)
            drawGridLines(context)
        Quartz.CGContextRestoreGState(context)
    
        Quartz.CGContextSaveGState(context)
        if 1:
            # The current text position is that after the last piece
            # of text has been drawn. Since CGContextSaveGState/
            # CGContextRestoreGState do not affect the text position or
            # the text matrix, the text position is that after the last
            # text was "drawn", that drawn with the kCGTextClip mode
            # above. This is where the next text drawn will go if it
            # isn't explicitly positioned.
            nextTextPosition = Quartz.CGContextGetTextPosition(context)
    
            # Draw so that the text is filled, stroked, and then used
            # the clip subsequent drawing.
            Quartz.CGContextSetTextDrawingMode(context, Quartz.kCGTextFillStrokeClip)
    
            # Explicitly set the text position.
            Quartz.CGContextSetTextPosition(context, 180, 400)
            nextTextPosition = Quartz.CGContextGetTextPosition(context)
    
            Quartz.CGContextShowText(context, fillStrokeClipText, len(fillStrokeClipText))
            # Adjust the location of the grid lines so that they overlap the
            # text just drawn.
            Quartz.CGContextTranslateCTM(context, nextTextPosition.x, nextTextPosition.y)
            # Draw the grid lines clipped by the text.
            drawGridLines(context)
        Quartz.CGContextRestoreGState(context)
    
    
    # showFlippedTextAtPoint is a cover routine for Quartz.CGContextShowText
    # that is useful for drawing text in a coordinate system where the y axis
    # is flipped relative to the default Quartz coordinate system.
    #
    # This code assumes that the text matrix is only used to
    # flip the text, not to perform scaling or any other
    # possible use of the text matrix.
    #
    # This function preserves the a, b, c, and d components of
    # the text matrix across its execution but updates the
    # tx, ty components (the text position) to reflect the
    # text just drawn. If all the text you draw is flipped, it
    # isn't necessary to continually set the text matrix. Instead
    # you could simply call CGContextSetTextMatrix once with
    # the flipped matrix each time your drawing
    # code is called.
    def showFlippedTextAtPoint(c, x, y, text, textLen):
        t = Quartz.CGAffineTransform(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)
        # Get the existing text matrix.
        s = Quartz.CGContextGetTextMatrix(c)
        # Set the text matrix to the one that flips in y.
        Quartz.CGContextSetTextMatrix(c, t)
        # Draw the text at the point.
        Quartz.CGContextShowTextAtPoint(c, x, y, text, textLen)
        # Get the updated text position.
        p = Quartz.CGContextGetTextPosition(c)
        # Update the saved text matrix to reflect the updated
        # text position.
        s.tx = p.x
        s.ty = p.y
        # Reset to the text matrix in effect when this
        # routine was called but with the text position updated.
        Quartz.CGContextSetTextMatrix(c, s)
    
    
    def drawQuartzTextWithTextMatrix(context):
        fontSize = 60.0
        extraLeading = 10.0
        text = b"Quartz "
        textlen = len(text)
    
        # The Cocoa framework calls the draw method with an undefined
        # value of the text matrix. It's best to set it to what is needed by
        # this code. Initially that is the identity transform.
        Quartz.CGContextSetTextMatrix(context, Quartz.CGAffineTransformIdentity)
    
        # Set the font with the PostScript name "Times-Roman", at
        # fontSize points, with the MacRoman encoding.
        Quartz.CGContextSelectFont(
            context, b"Times-Roman", fontSize, Quartz.kCGEncodingMacRoman
        )
    
        # ----  Text Line 1 ----
    
        # Draw the text at (10, 600).
        Quartz.CGContextShowTextAtPoint(context, 10, 600, text, textlen)
    
        # Get the current text position. The text pen is at the trailing
        # point from the text just drawn.
        textPosition = Quartz.CGContextGetTextPosition(context)
    
        # Set the text matrix to one that flips text in y and sets
        # the text position to the user space coordinate (0,0).
        t = Quartz.CGAffineTransformMake(1, 0, 0, -1, 0, 0)
        Quartz.CGContextSetTextMatrix(context, t)
    
        # Set the text position to the point where the previous text ended.
        Quartz.CGContextSetTextPosition(context, textPosition.x, textPosition.y)
    
        # Draw the text at the current text position. It will be drawn
        # flipped in y, relative to the text drawn previously.
        Quartz.CGContextShowText(context, text, textlen)
    
        # ----  Text Line 2 ----
    
        # Translate down for the next piece of text.
        Quartz.CGContextTranslateCTM(context, 0, -(3 * fontSize + extraLeading))
    
        Quartz.CGContextSaveGState(context)
        if 1:
            # Change the text matrix to {1, 0, 0, 3, 0, 0}, which
            # scales text by a factor of 1 in x and 3 in y.
            # This scaling doesn't affect any drawing other than text
            # drawing since only text drawing is transformed by
            # the text matrix.
            t = Quartz.CGAffineTransformMake(1, 0, 0, 3, 0, 0)
            Quartz.CGContextSetTextMatrix(context, t)
    
            # This text is scaled relative to the previous text
            # because of the text matrix scaling.
            Quartz.CGContextShowTextAtPoint(context, 10, 600, text, textlen)
    
        # This restores the graphics state to what it was at the time
        # of the last Quartz.CGContextSaveGState, but since the text matrix
        # isn't part of the Quartz graphics state, it isn't affected.
        Quartz.CGContextRestoreGState(context)
    
        # The text matrix isn't affected by Quartz.CGContextSaveGState and
        # Quartz.CGContextRestoreGState. You can see this by observing that
        # the next text piece appears immediately after the first piece
        # and with the same text scaling as that text drawn with the
        # text matrix established before we did CGContextRestoreGState.
        Quartz.CGContextShowText(context, text, textlen)
    
        # ----  Text Line 3 ----
        # Translate down for the next piece of text.
        Quartz.CGContextTranslateCTM(context, 0, -(fontSize + extraLeading))
    
        # Reset the text matrix to the identity matrix.
        Quartz.CGContextSetTextMatrix(context, Quartz.CGAffineTransformIdentity)
    
        # Now draw text in a flipped coordinate system.
        Quartz.CGContextSaveGState(context)
        if 1:
            # Flip the coordinate system to mimic a coordinate system with the origin
            # at the top-left corner of a window. The new origin is at 600 units in
            # +y from the old origin and the y axis now increases with positive y
            # going down the window.
            Quartz.CGContextConcatCTM(
                context, Quartz.CGAffineTransformMake(1, 0, 0, -1, 0, 600)
            )
            # This text will be flipped along with the CTM.
            Quartz.CGContextShowTextAtPoint(context, 10, 10, text, textlen)
            # Obtain the user space coordinates of the current text position.
            textPosition = Quartz.CGContextGetTextPosition(context)
            # Draw text at that point but flipped in y.
            showFlippedTextAtPoint(context, textPosition.x, textPosition.y, text, textlen)
        Quartz.CGContextRestoreGState(context)

.. rst-class:: tabbertab

Shadings.py
...........

.. sourcecode:: python

    import Quartz
    import Utilities
    
    
    def RedBlackRedRampEvaluate(info, input_value, output_value):
        # The domain of this function is 0 - 1. For an input value of 0
        # this function returns the color to paint at the start point
        # of the shading. For an input value of 1 this function returns
        # the color to paint at the end point of the shading. This
        # is a 1 in, 4 out function where the output values correspond
        # to an r,g,b,a color.
        #
        # For an RGB color space as the shading color space, this
        # function evaluates to produce a blend from pure, opaque
        # red at the start point to a pure opaque black at the
        # midpoint, and back to pure opaque red at the end point.
    
        return (
            # The red component evaluates to 1 for an input value of 0
            # (the start point of the shading). It smoothly reduces
            # to zero at the midpoint of the shading (input value 0.5)
            # and increases up to 1 at the endpoint of the shading (input
            # value 1.0).
            abs(1.0 - input_value[0] * 2),
            # The green and blue components are always 0.
            0,
            0,
            # The alpha component is 1 for the entire shading.
            1,
        )
    
    
    def createFunctionForRGB(evaluationFunction):
        # This is a 1 in, 4 out function for drawing shadings
        # in a 3 component (plus alpha) color space. Shadings
        # parameterize the endpoints such that the starting point
        # represents the function input value 0 and the ending point
        # represents the function input value 1.
        domain = (0, 1)
    
        # The range is the range for the output colors. For an rgb
        # color space the values range from 0-1 for the r,g,b, and a
        # components.
    
        function_range = (
            # The red component, min and max.
            0,
            1,
            # The green component, min and max.
            0,
            1,
            # The blue component, min and max.
            0,
            1,
            # The alpha component, min and max.
            0,
            1,
        )
    
        # Dimension of domain is 1 and dimension of range is 4.
        function = Quartz.CGFunctionCreate(
            None, 1, domain, 4, function_range, evaluationFunction
        )
    
        if function is None:
            print("Couldn't create the CGFunction!")
            return None
    
        return function
    
    
    def doSimpleAxialShading(context):
        # This shading paints colors in the calibrated Generic RGB
        # color space so it needs a function that evaluates 1 in to 4 out.
        axialFunction = createFunctionForRGB(RedBlackRedRampEvaluate)
        if axialFunction is None:
            return
    
        # Start the shading at the point (20,20) and
        # end it at (420,20). The axis of the shading
        # is a line from (20,20) to (420,20).
        startPoint = Quartz.CGPoint(x=20, y=20)
        endPoint = Quartz.CGPoint(x=420, y=20)
    
        # Don't extend this shading.
        extendStart = extendEnd = False
    
        shading = Quartz.CGShadingCreateAxial(
            Utilities.getTheCalibratedRGBColorSpace(),
            startPoint,
            endPoint,
            axialFunction,
            extendStart,
            extendEnd,
        )
        # The shading retains the function and this code
        # is done with the function so it should release it.
        del axialFunction
        if shading is None:
            print("Couldn't create the shading!")
            return
    
        # Draw the shading. This paints the shading to
        # the destination context, clipped by the
        # current clipping area.
        Quartz.CGContextDrawShading(context, shading)
    
    
    def RedGreenRampEvaluate(info, input_value, output_value):
        # The domain of this function is 0 - 1. For an input value of 0
        # this function returns the color to paint at the start point
        # of the shading. For an input value of 1 this function returns
        # the color to paint at the end point of the shading. This
        # is a 1 in, 4 out function where the output values correspond
        # to an r,g,b,a color.
        #
        # For an RGB color space as the shading color space, this
        # function evaluates to produce a blend from pure, opaque
        # red at the start point to a pure opaque green at the end point.
    
        return (
            # The red component starts at 1 and reduces to zero as the input
            # goes from 0 (the start point of the shading) and increases
            # to 1 (the end point of the shading).
            1.0 - input_value[0],
            # The green component starts at 0 for an input of 0
            # (the start point of the shading) and increases to 1
            # for an input value of 1 (the end point of the shading).
            input_value[0],
            # The blue component is always 0.
            0,
            # The alpha component is always 1, the shading is always opaque.
            1,
        )
    
    
    def doExampleAxialShading(context):
        rect = Quartz.CGRectMake(0, 0, 240, 240)
    
        # This shading paints colors in the calibrated Generic RGB
        # color space so it needs a function that evaluates 1 in to 4 out.
        redGreenFunction = createFunctionForRGB(RedGreenRampEvaluate)
        if redGreenFunction is None:
            return
    
        # Start the shading at the point (20,20) and
        # end it at (220,220). The axis of the shading
        # is a diagonal line from (20,20) to (220,220).
        startPoint = Quartz.CGPoint(x=20, y=20)
        endPoint = Quartz.CGPoint(x=220, y=220)
    
        # Don't extend this shading.
        extendStart = extendEnd = False
        shading = Quartz.CGShadingCreateAxial(
            Utilities.getTheCalibratedRGBColorSpace(),
            startPoint,
            endPoint,
            redGreenFunction,
            extendStart,
            extendEnd,
        )
    
        if shading is None:
            print("Couldn't create the shading!")
            return
    
        # Position for the first portion of the drawing.
        Quartz.CGContextTranslateCTM(context, 40, 260)
    
        # Stroke a black rectangle that will frame the shading.
        Quartz.CGContextSetLineWidth(context, 2)
        Quartz.CGContextSetStrokeColorWithColor(context, Utilities.getRGBOpaqueBlackColor())
        Quartz.CGContextStrokeRect(context, rect)
    
        Quartz.CGContextSaveGState(context)
        if 1:
            # Clip to the rectangle that was just stroked.
            Quartz.CGContextClipToRect(context, rect)
            # Draw the shading. This paints the shading to
            # the destination context, clipped to rect.
            Quartz.CGContextDrawShading(context, shading)
            # Release the shading once the code is finished with it.
            del shading
            # Restore the graphics state so that the rectangular
            # clip is no longer present.
        Quartz.CGContextRestoreGState(context)
    
        # Prepare for the next shading.
        Quartz.CGContextTranslateCTM(context, 0, -250)
    
        # Extend this shading.
        extendStart = extendEnd = True
        shading = Quartz.CGShadingCreateAxial(
            Utilities.getTheCalibratedRGBColorSpace(),
            startPoint,
            endPoint,
            redGreenFunction,
            extendStart,
            extendEnd,
        )
        # The shading retains the function and this code
        # is done with the function so it should release it.
        del redGreenFunction
        if shading is None:
            print("Couldn't create the shading!")
            return
    
        # Stroke with the current stroke color.
        Quartz.CGContextStrokeRect(context, rect)
    
        Quartz.CGContextSaveGState(context)
        if 1:
            Quartz.CGContextClipToRect(context, rect)
            # Draw the shading. This paints the shading to
            # the destination context, clipped to rect.
            Quartz.CGContextDrawShading(context, shading)
        Quartz.CGContextRestoreGState(context)
    
        # Now paint some text with a shading.
        Quartz.CGContextSaveGState(context)
        if 1:
            Quartz.CGContextTranslateCTM(context, 260, 0)
            Quartz.CGContextSetTextMatrix(context, Quartz.CGAffineTransformIdentity)
    
            # Set the font with the PostScript name "Times-Roman", at
            # 80 points, with the MacRoman encoding.
            Quartz.CGContextSelectFont(
                context, b"Times-Roman", 80, Quartz.kCGEncodingMacRoman
            )
    
            # Rotate so that the text characters are rotated
            # relative to the page.
            Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(45))
            # Set the text drawing mode to clip so that
            # the characters in the string are intersected with
            # the clipping area.
            Quartz.CGContextSetTextDrawingMode(context, Quartz.kCGTextClip)
            Quartz.CGContextShowTextAtPoint(context, 30, 0, b"Shading", 7)
    
            # At this point nothing has been painted; the
            # glyphs in the word "Shading" have been intersected
            # with the previous clipping area to create a new
            # clipping area.
    
            # Rotate the coordinate system back so that the
            # shading is not rotated relative to the page.
            Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(-45))
    
            # Draw the shading, painting the shading
            # to the destination context, clipped by the glyphs.
            Quartz.CGContextDrawShading(context, shading)
    
        Quartz.CGContextRestoreGState(context)
        # Release the shading once the code is finished with it.
        del shading
    
    
    class MyStartEndColor:
        def __init__(self):
            self.startColor = [0.0] * 3
            self.endColor = [0.0] * 3
    
    
    def StartColorEndColorEvaluate(info, input_value, output_value):
        # The domain of this function is 0 - 1. For an input value of 0
        # this function returns the color to paint at the start point
        # of the shading. For an input value of 1 this function returns
        # the color to paint at the end point of the shading. This
        # is a 1 in, 4 out function where the output values correspond
        # to an r,g,b,a color.
        #
        # This function evaluates to produce a blend from startColor to endColor.
        #
        # Note that the returned results are clipped to the range
        # by Quartz so this function doesn't worry about values
        # that are outside the range 0-1.
    
        # Weight the starting and ending color components depending
        # on what position in the blend the input value specifies.
        return (
            (info.startColor[0] * (1 - input_value[0]) + info.endColor[0] * input_value[0]),
            (info.startColor[1] * (1 - input_value[0]) + info.endColor[1] * input_value[0]),
            (info.startColor[2] * (1 - input_value[0]) + info.endColor[2] * input_value[0]),
            # The alpha component is always 1, the shading is always opaque.
            1,
        )
    
    
    def createFunctionWithStartEndColorRamp(startColor, endColor):
        # Use a pointer to a MyStartEndColor as a way of
        # parameterizing the color ramp this function produces.
        startEndColorP = MyStartEndColor()
    
        # Set up start and end colors in the info structure.
        startEndColorP.startColor[0] = startColor[0]
        startEndColorP.startColor[1] = startColor[1]
        startEndColorP.startColor[2] = startColor[2]
    
        startEndColorP.endColor[0] = endColor[0]
        startEndColorP.endColor[1] = endColor[1]
        startEndColorP.endColor[2] = endColor[2]
    
        # This is a 1 in, 4 out function for drawing shadings
        # in a 3 component (plus alpha) color space. Shadings
        # parameterize the endpoints such that the starting point
        # represents the function input value 0 and the ending point
        # represents the function input value 1.
        domain = (0, 1)
    
        # The range is the range for the output colors. For an rgb
        # color space the values range from 0-1 for the r,g,b, and a
        # components.
    
        function_range = (
            # The red component, min and max.
            0,
            1,
            # The green component, min and max.
            0,
            1,
            # The blue component, min and max.
            0,
            1,
            # The alpha component, min and max.
            0,
            1,
        )
    
        # Pass startEndColorP as the info parameter.
        function = Quartz.CGFunctionCreate(
            startEndColorP, 1, domain, 4, function_range, StartColorEndColorEvaluate
        )
    
        if function is None:
            print("Couldn't create the CGFunction!")
            return None
    
        return function
    
    
    def doSimpleRadialShading(context):
        startColor = [0.663, 0.0, 0.031]  # Red.
        endColor = [1.0, 0.8, 0.4]  # Light yellow.
    
        # This function describes a color ramp where the starting color
        # is red and the ending color is blue.
        redYellowFunction = createFunctionWithStartEndColorRamp(startColor, endColor)
        if redYellowFunction is None:
            return
    
        Quartz.CGContextTranslateCTM(context, 120, 120)
    
        # Circles whose origin is the same.
        circleACenter = Quartz.CGPoint(x=0, y=0)
        circleBCenter = circleACenter
    
        # The starting circle is inside the ending circle.
        circleARadius = 50
        circleBRadius = 100
    
        #  Don't extend the shading.
        extendStart = extendEnd = False
        shading = Quartz.CGShadingCreateRadial(
            Utilities.getTheCalibratedRGBColorSpace(),
            circleACenter,
            circleARadius,
            circleBCenter,
            circleBRadius,
            redYellowFunction,
            extendStart,
            extendEnd,
        )
    
        del redYellowFunction
        if shading is None:
            print("Couldn't create the shading!")
            return
        Quartz.CGContextDrawShading(context, shading)
    
    
    def doExampleRadialShadings(context):
        magenta = [1, 0, 1]  # Pure magenta.
        magenta30 = [0.3, 0, 0.3]  # 30% magenta.
        black = [0, 0, 0]
        red = [1, 0, 0]
        green = [0, 1, 0]
        blue = [0, 0, 1]
        redgreen = [0.66, 1, 0.04]  # A red-green shade.
    
        Quartz.CGContextTranslateCTM(context, 120, 550)
    
        # This function describes a color ramp where the starting color
        # is a full magenta, the ending color is 30% magenta.
        magentaFunction = createFunctionWithStartEndColorRamp(magenta, magenta30)
        if magentaFunction is None:
            print("Couldn't create the magenta function!")
            return
    
        # Shading 1. Circle A is completely inside circle B but with
        # different origins. Circle A has radius 0 which produces
        # a point source.
    
        # The center of circle A is offset from the origin.
        circleACenter = Quartz.CGPoint(x=30, y=40)
        # The center of circle B is at the origin.
        circleBCenter = Quartz.CGPoint(x=0, y=0)
    
        # A radius of zero produces a point source.
        circleARadius = 0
        circleBRadius = 100
    
        # Don't extend the shading.
        extendStart = extendEnd = False
        shading = Quartz.CGShadingCreateRadial(
            Utilities.getTheCalibratedRGBColorSpace(),
            circleACenter,
            circleARadius,
            circleBCenter,
            circleBRadius,
            magentaFunction,
            extendStart,
            extendEnd,
        )
        # Finished with the magenta function so release it.
        del magentaFunction
        if shading is None:
            print("Couldn't create the shading!")
            return
    
        Quartz.CGContextDrawShading(context, shading)
        # Finished with the shading so release it.
        del shading
    
        # Shading 2. Circle A is completely inside
        # circle B but with different origins.
    
        # The starting color is red and the ending color is green.
        redGreenFunction = createFunctionWithStartEndColorRamp(red, green)
        if redGreenFunction is None:
            print("Couldn't create the red-Green function!")
            return
    
        circleACenter.x = 55
        circleACenter.y = 70
        circleBCenter.x = 20
        circleBCenter.y = 0
        circleARadius = 10
        # The outer circle is outside the clipping path so the
        # color at the edge of the shape is not
        # that at the radius of the outer circle.
        circleBRadius = 200
        # Extend the end point of this shading.
        extendStart = False
        extendEnd = True
        shading = Quartz.CGShadingCreateRadial(
            Utilities.getTheCalibratedRGBColorSpace(),
            circleACenter,
            circleARadius,
            circleBCenter,
            circleBRadius,
            redGreenFunction,
            extendStart,
            extendEnd,
        )
        # Finished with this function so release it.
        del redGreenFunction
        if shading is None:
            print("Couldn't create the shading!")
            return
    
        # Set a clipping area to bound the extend. This code
        # sets a clipping area that corresponds to a circular
        # wedge. The starting circle is inside the clipping
        # area and the ending circle is outside.
        Quartz.CGContextSaveGState(context)
        if 1:
            Quartz.CGContextTranslateCTM(context, 250, 0)
            Quartz.CGContextBeginPath(context)
            Quartz.CGContextMoveToPoint(context, 25, 0)
            Quartz.CGContextAddArc(
                context,
                25,
                0,
                130,
                Utilities.DEGREES_TO_RADIANS(30),
                Utilities.DEGREES_TO_RADIANS(-30),
                0,
            )
            Quartz.CGContextClip(context)
            # Paint the shading.
            Quartz.CGContextDrawShading(context, shading)
            # Finished with the shading so release it.
            del shading
        Quartz.CGContextRestoreGState(context)
    
        Quartz.CGContextTranslateCTM(context, -40, -250)
    
        # Shading 3. The starting circle is completely outside
        # the ending circle, no extension. The circles
        # have the same radii.
        circleACenter.x = 0
        circleACenter.y = 0
        circleBCenter.x = 125
        circleBCenter.y = 0
    
        circleARadius = 50
        circleBRadius = 50
    
        extendStart = extendEnd = False
    
        # Create a function that paints a red to black ramp.
        redBlackFunction = createFunctionWithStartEndColorRamp(red, black)
        if redBlackFunction is None:
            print("Couldn't create the red-black function!")
            return
    
        shading = Quartz.CGShadingCreateRadial(
            Utilities.getTheCalibratedRGBColorSpace(),
            circleACenter,
            circleARadius,
            circleBCenter,
            circleBRadius,
            redBlackFunction,
            extendStart,
            extendEnd,
        )
        if shading is None:
            print("Couldn't create the shading!")
            return
    
        Quartz.CGContextDrawShading(context, shading)
        # Finished with the shading so release it.
        del shading
    
        # Shading 4. The starting circle is completely outside
        # the ending circle. The circles have different radii.
        circleACenter.x = 120
        circleACenter.y = 0
        circleBCenter.x = 0
        circleBCenter.y = 0
    
        circleARadius = 75
        circleBRadius = 30
    
        # Extend at the start and end.
        extendStart = extendEnd = True
        shading = Quartz.CGShadingCreateRadial(
            Utilities.getTheCalibratedRGBColorSpace(),
            circleACenter,
            circleARadius,
            circleBCenter,
            circleBRadius,
            redBlackFunction,
            extendStart,
            extendEnd,
        )
        # Finished with this function so release it.
        del redBlackFunction
        if shading is None:
            print("Couldn't create the shading!")
            return
    
        Quartz.CGContextSaveGState(context)
        if 1:
            Quartz.CGContextTranslateCTM(context, 270, 0)
            # Clip to an elliptical path so the shading
            # does not extend to infinity at the larger end.
            Quartz.CGContextBeginPath(context)
            Utilities.myCGContextAddEllipseInRect(
                context, Quartz.CGRectMake(-200, -200, 450, 400)
            )
            Quartz.CGContextClip(context)
            Quartz.CGContextDrawShading(context, shading)
            # Finished with the shading so release it.
            del shading
        Quartz.CGContextRestoreGState(context)
    
        Quartz.CGContextTranslateCTM(context, 30, -200)
    
        # The starting color is blue, the ending color is a red-green color.
        blueGreenFunction = createFunctionWithStartEndColorRamp(blue, redgreen)
        if blueGreenFunction is None:
            print("Couldn't create the blue-Green function!")
            return
    
        # Shading 5. The circles partially overlap and have
        # different radii with the larger circle at the start.
        circleACenter.x = 0
        circleACenter.y = 0
        circleBCenter.x = 90
        circleBCenter.y = 30
    
        circleARadius = 75
        circleBRadius = 45
    
        extendStart = extendEnd = False
        shading = Quartz.CGShadingCreateRadial(
            Utilities.getTheCalibratedRGBColorSpace(),
            circleACenter,
            circleARadius,
            circleBCenter,
            circleBRadius,
            blueGreenFunction,
            extendStart,
            extendEnd,
        )
        if shading is None:
            print("Couldn't create the shading!")
            return
    
        Quartz.CGContextDrawShading(context, shading)
        # Finished with the shading so release it.
        del shading
    
        Quartz.CGContextTranslateCTM(context, 200, 0)
    
        # Shading 6. The circles partially overlap and have
        # different radii with the larger circle at the end.
    
        circleARadius = 45
        circleBRadius = 75
        shading = Quartz.CGShadingCreateRadial(
            Utilities.getTheCalibratedRGBColorSpace(),
            circleACenter,
            circleARadius,
            circleBCenter,
            circleBRadius,
            blueGreenFunction,
            extendStart,
            extendEnd,
        )
        # Finished with this function so release it.
        del blueGreenFunction
        if shading is None:
            print("Couldn't create the shading!")
            return
    
        Quartz.CGContextDrawShading(context, shading)
    
    
    def doEllipseShading(context):
        black = [0, 0, 0]
        red = [1, 0, 0]
    
        # This function describes a color ramp where the starting color
        # is red, the ending color is black.
        redBlackFunction = createFunctionWithStartEndColorRamp(red, black)
        if redBlackFunction is None:
            print("Couldn't create the red-black function!")
            return
    
        Quartz.CGContextTranslateCTM(context, 100, 300)
        # Shading 1.
        #   To obtain an elliptical shading requires that user space
        #   at the time the shading is painted is transformed so that
        #   the circles which define the radial shading geometry are
        #   rotated and elliptical. User space will be rotated
        #   by 45 degrees, then scaled by 1 in x and 2 in y to produce
        #   the ellipses.
    
        # Compute the transform needed to create the rotated ellipses.
        t = Quartz.CGAffineTransformMakeRotation(Utilities.DEGREES_TO_RADIANS(45))
        t = Quartz.CGAffineTransformScale(t, 1, 2)
    
        circleACenter = Quartz.CGPoint(x=0, y=0)
        circleBCenter = Quartz.CGPoint(x=circleACenter.x + 144, y=circleACenter.y)
        circleARadius = 45
        circleBRadius = 45
    
        # Don't extend this shading.
        extendStart = extendEnd = False
    
        shading = Quartz.CGShadingCreateRadial(
            Utilities.getTheCalibratedRGBColorSpace(),
            circleACenter,
            circleARadius,
            circleBCenter,
            circleBRadius,
            redBlackFunction,
            extendStart,
            extendEnd,
        )
        if shading is None:
            # Couldn't create the shading so release
            # the function before returning.
            print("Couldn't create the shading!")
            return
    
        Quartz.CGContextSaveGState(context)
        if 1:
            # Transform coordinates for the drawing of the shading.
            # This transform produces the rotated elliptical shading.
            # This produces the left shading in the figure, the
            # one where both the ellipses and the shading are
            # rotated relative to default user space.
            Quartz.CGContextConcatCTM(context, t)
    
            Quartz.CGContextDrawShading(context, shading)
            del shading
        Quartz.CGContextRestoreGState(context)
    
        Quartz.CGContextTranslateCTM(context, 300, 10)
    
        # Shading 2.
        #    Now draw the shading where the shading ellipses are
        #    rotated but the axis between the origins of
        #    the ellipses lies parallel to the x axis in default
        #    user space. This is similar to the shading drawn
        #    manually in Chapter 5.
        #
        #    To compute the correct origins for the shading,
        #    the code needs to compute the points that,
        #    transformed by the matrix t used to paint the shading,
        #    produce the desired coordinates. We want coordinates
        #    that are transformed as follows:
        #
        #            P' = P x t
        #
        #    where P' is the point in untransformed user space that
        #    we want as the origin, P is the point in transformed
        #    user space that will be transformed by t, the matrix
        #    which transforms the circles into rotated ellipses.
        #
        #    So we want to calculate P such that P' = P x t .
        #
        #    Notice that if P = P' x Inverse(t) then:
        #
        #    P' = P' x Inverse(t) x t = P' x Identity = P'.
        #
        #    This means that we can calculate the point P
        #    by computing P' x Inverse(t).
    
        inverseT = Quartz.CGAffineTransformInvert(t)
        # Now the code can transform the coordinates through the
        # inverse transform to compute the new coordinates. These
        # coordinates, when transformed with the transform t,
        # produce the original coordinate.
        circleACenter = Quartz.CGPointApplyAffineTransform(circleACenter, inverseT)
        circleBCenter = Quartz.CGPointApplyAffineTransform(circleBCenter, inverseT)
    
        shading = Quartz.CGShadingCreateRadial(
            Utilities.getTheCalibratedRGBColorSpace(),
            circleACenter,
            circleARadius,
            circleBCenter,
            circleBRadius,
            redBlackFunction,
            extendStart,
            extendEnd,
        )
        # The code is finished with the function so release it.
        del redBlackFunction
        if shading is None:
            print("Couldn't create the shading!")
            return
    
        # Transform coordinates for the drawing of the shading.
        # This transform produces the rotated elliptical shading.
        Quartz.CGContextConcatCTM(context, t)
    
        Quartz.CGContextDrawShading(context, shading)

.. rst-class:: tabbertab

ShadowsAndTransparencyLayers.py
...............................

.. sourcecode:: python

    import Quartz
    import Utilities
    
    
    def scaleShadowOffset(offset):
        shadowScaling = Utilities.getScalingFactor()
        # Adjust the shadow offset if scaling to export as bits. This is
        # equivalent to scaling base space by the scaling factor.
        if shadowScaling != 1.0:
            offset = Quartz.CGSizeApplyAffineTransform(
                offset, Quartz.CGAffineTransformMakeScale(shadowScaling, shadowScaling)
            )
        return offset
    
    
    def createTrianglePath(context):
        Quartz.CGContextBeginPath(context)
        Quartz.CGContextMoveToPoint(context, 0, 0)
        Quartz.CGContextAddLineToPoint(context, 50, 0)
        Quartz.CGContextAddLineToPoint(context, 25, 50)
        Quartz.CGContextClosePath(context)
    
    
    def drawSimpleShadow(context):
        r = Quartz.CGRectMake(20, 20, 100, 200)
    
        Quartz.CGContextTranslateCTM(context, 20, 300)
    
        # A blur of 0 is a hard edge blur.
        blur = 0
        # An offset where both components are negative casts a shadow to the
        # left and down from the object. The coordinate system for the offset
        # is base space, not current user space.
        offset = Quartz.CGSize(-7, -7)
        offset = scaleShadowOffset(offset)
    
        # Set the shadow in the context.
        Quartz.CGContextSetShadow(context, offset, blur)
    
        # Object 1.
        # Paint a rectangle.
        Quartz.CGContextFillRect(context, r)
    
        # Object 2.
        Quartz.CGContextTranslateCTM(context, 150, 0)
        # A blur of 3 is a soft blur more
        # appropriate for a shadow effect.
        blur = 3
        Quartz.CGContextSetShadow(context, offset, blur)
    
        # Fill an ellipse to the right of the rect.
        Quartz.CGContextBeginPath(context)
        Utilities.myCGContextAddEllipseInRect(context, r)
        Quartz.CGContextFillPath(context)
    
        # Object 3.
        Quartz.CGContextTranslateCTM(context, -130, -140)
        # Scale the coordinate system but the shadow is not affected. The offset
        # is in the base space of the context. Typically it looks best if the shapes
        # have a uniform shadow regardless of how the shapes were created, scaled,
        # rotated, or otherwise transformed.
        Quartz.CGContextScaleCTM(context, 2, 2)
        createTrianglePath(context)
        Quartz.CGContextSetStrokeColorWithColor(context, Utilities.getRGBOpaqueRedColor())
    
        Quartz.CGContextSetLineWidth(context, 5)
        # Stroking produces a shadow as well.
        Quartz.CGContextStrokePath(context)
    
        # Object 4.
        Quartz.CGContextTranslateCTM(context, 75, 0)
        createTrianglePath(context)
        # Cast the shadow to the left and up from
        # the shape painted.
        offset.width = -5
        offset.height = +7
        offset = scaleShadowOffset(offset)
    
        # The shadow can be colored. Create a CGColorRef
        # that represents a red color with opacity of 0.3333...
        shadowColor = Quartz.CGColorCreateCopyWithAlpha(
            Utilities.getRGBOpaqueRedColor(), 1.0 / 3.0
        )
    
        Quartz.CGContextSetShadowWithColor(context, offset, blur, shadowColor)
        Quartz.CGContextStrokePath(context)
    
        # Object 5. Three stroked circles.
        Quartz.CGContextTranslateCTM(context, -75, -65)
        # Set a black shadow offset at -7,-7.
        offset.width = -7
        offset.height = -7
    
        offset = scaleShadowOffset(offset)
    
        Quartz.CGContextSetShadow(context, offset, blur)
        # Draw a set of three circles side by side.
        Quartz.CGContextBeginPath(context)
        Quartz.CGContextSetLineWidth(context, 3)
        r = Quartz.CGRectMake(30, 20, 20, 20)
        Utilities.myCGContextAddEllipseInRect(context, r)
        r = Quartz.CGRectOffset(r, 20, 0)
        Utilities.myCGContextAddEllipseInRect(context, r)
        r = Quartz.CGRectOffset(r, 20, 0)
        Utilities.myCGContextAddEllipseInRect(context, r)
        Quartz.CGContextStrokePath(context)
    
    
    def doShadowScaling(context):
        offset = Quartz.CGSize(-7, -7)
        blur = 3
    
        Quartz.CGContextTranslateCTM(context, 20, 220)
        Quartz.CGContextSetShadow(context, scaleShadowOffset(offset), blur)
    
        # Object 1
        # Draw a triangle filled with black and shadowed with black.
        createTrianglePath(context)
        Quartz.CGContextFillPath(context)
    
        # Object 2
        # Scaling without changing the shadow doesn't impact
        # the shadow offset or blur.
        t = Quartz.CGAffineTransformMakeScale(2, 2)
        Quartz.CGContextConcatCTM(context, t)
        Quartz.CGContextTranslateCTM(context, 40, 0)
        createTrianglePath(context)
        Quartz.CGContextFillPath(context)
    
        # Object 3
        # By transforming the offset you can transform the shadow.
        # This may be desirable if you are drawing a zoomed view.
        offset = Quartz.CGSizeApplyAffineTransform(offset, t)
        Quartz.CGContextSetShadow(context, scaleShadowOffset(offset), blur)
        Quartz.CGContextTranslateCTM(context, 70, 0)
        createTrianglePath(context)
        Quartz.CGContextFillPath(context)
    
    
    def drawFillAndStrokeWithShadow(context):
        r = Quartz.CGRectMake(60, 60, 100, 100)
        offset = Quartz.CGSize(-7, -7)
        blur = 3
    
        # Set the shadow.
        Quartz.CGContextSetShadow(context, scaleShadowOffset(offset), blur)
    
        Quartz.CGContextSetFillColorWithColor(context, Utilities.getRGBOpaqueOrangeColor())
    
        # Draw the graphic on the left.
        Quartz.CGContextBeginPath(context)
        Utilities.myCGContextAddEllipseInRect(context, r)
        Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
    
        # Draw the graphic on the right.
        r = Quartz.CGRectOffset(r, 125, 0)
        # Begin the transparency layer.
        Quartz.CGContextBeginTransparencyLayer(context, None)
        if 1:
            Utilities.myCGContextAddEllipseInRect(context, r)
            Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
        # End the transparency layer.
        Quartz.CGContextEndTransparencyLayer(context)
    
    
    def drawColoredLogo(context):
        r = Quartz.CGRectMake(0, 0, 100, 100)
        Quartz.CGContextSaveGState(context)
        if 1:
            # Position the center of the rectangle on the left.
            Quartz.CGContextTranslateCTM(context, 140, 140)
            # Rotate so that the rectangles are rotated 45 degrees
            # about the current coordinate origin.
            Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(45))
            # Translate so that the center of the rect is at the previous origin.
            Quartz.CGContextTranslateCTM(context, -r.size.width / 2, -r.size.height / 2)
            # Set the fill color to a purple color.
            Quartz.CGContextSetFillColorWithColor(
                context, Utilities.getRGBOpaquePurpleColor()
            )
            # Fill the first rectangle.
            Quartz.CGContextFillRect(context, r)
            # Position to draw the right-most rectangle.
            Quartz.CGContextTranslateCTM(context, 60, -60)
            # Set the fill color to a yellow color.
            Quartz.CGContextSetFillColorWithColor(
                context, Utilities.getRGBOpaqueYellowColor()
            )
            Quartz.CGContextFillRect(context, r)
    
            # Position for the center rectangle.
            Quartz.CGContextTranslateCTM(context, -30, +30)
            # Set the stroke color to an orange color.
            Quartz.CGContextSetStrokeColorWithColor(
                context, Utilities.getRGBOpaqueOrangeColor()
            )
            # Stroke the rectangle with a linewidth of 12.
            Quartz.CGContextStrokeRectWithWidth(context, r, 12)
        Quartz.CGContextRestoreGState(context)
    
    
    def showComplexShadowIssues(context):
        offset = Quartz.CGSize(-6, -6)
        blur = 3
    
        # Set the shadow.
        Quartz.CGContextSetShadow(context, scaleShadowOffset(offset), blur)
        # Draw the colored logo.
        drawColoredLogo(context)
    
    
    def showComplexShadow(context):
        offset = Quartz.CGSize(-6, -6)
        blur = 3
    
        # Set the shadow.
        Quartz.CGContextSetShadow(context, scaleShadowOffset(offset), blur)
    
        # Begin a transparency layer. A snapshot is made of the graphics state and
        # the shadow parameter is temporarily reset to no shadow, the blend mode
        # is set to Normal, and the global alpha parameter is set to 1.0.
        #
        # All drawing that occurs after CGContextBeginTransparencyLayer but before
        # CGContextEndTransparencyLayer is collected together and when
        # CGContextEndTransparencyLayer is called, Quartz composites the collected
        # drawing to the context, using the global alpha, blend mode, and shadow
        # that was in effect when CGContextBeginTransparencyLayer was called.
    
        Quartz.CGContextBeginTransparencyLayer(context, None)
        # Draw the colored logo.
        drawColoredLogo(context)
    
        # Ending the transparency layer causes all drawing in the transparency
        # layer to be composited with the global alpha, blend mode, and shadow
        # in effect at the time CGContextBeginTransparencyLayer was called.  The
        # graphics state is restored to that in effect when
        # CGContextBeginTransparencyLayer was called.
    
        # This restores the graphics state to that in effect
        # at the last call to CGContextBeginTransparencyLayer.
        Quartz.CGContextEndTransparencyLayer(context)
    
    
    def doLayerCompositing(context):
        r = Quartz.CGRectMake(40, 50, 142, 180)
        # Object 1.
        Quartz.CGContextTranslateCTM(context, 20, 20)
        Quartz.CGContextSetFillColorWithColor(context, Utilities.getRGBOpaqueGreenColor())
        # Draw a green background.
        Quartz.CGContextFillRect(context, r)
        # Draw the colored logo.
        drawColoredLogo(context)
    
        # Object 2.
        Quartz.CGContextTranslateCTM(context, 300, 0)
        Quartz.CGContextSetFillColorWithColor(context, Utilities.getRGBOpaqueGreenColor())
        # Draw a green background.
        Quartz.CGContextFillRect(context, r)
    
        # Draw the rectangles with opacity 0.75.
        Quartz.CGContextSetAlpha(context, 0.75)
    
        drawColoredLogo(context)
    
        # Object 3.
        Quartz.CGContextTranslateCTM(context, 300, 0)
        # Set the alpha to 1.0 for drawing the background.
        Quartz.CGContextSetAlpha(context, 1.0)
        Quartz.CGContextSetFillColorWithColor(context, Utilities.getRGBOpaqueGreenColor())
        Quartz.CGContextFillRect(context, r)
        # Draw the rectangles with opacity 0.75.
        Quartz.CGContextSetAlpha(context, 0.75)
        # Begin a transparency layer. Drawing collected in
        # this transparency layer will be composited with an
        # alpha value of 0.75 when the transparency layer is ended.
        Quartz.CGContextBeginTransparencyLayer(context, None)
        if 1:
            # Draw the colored logo into the transparency layer.
            drawColoredLogo(context)
    
        # Ending the transparency layer causes the drawing
        # to then be composited with the global alpha value
        # in effect when CGContextBeginTransparencyLayer was called.
        Quartz.CGContextEndTransparencyLayer(context)
    
    
    def shadowPDFDocument(context, url):
        pdfDoc = Quartz.CGPDFDocumentCreateWithURL(url)
        offset = Quartz.CGSize(-7, -7)
    
        if pdfDoc is None:
            print("Couldn't create PDF document reference!")
            return
    
        r = Quartz.CGPDFDocumentGetMediaBox(pdfDoc, 1)
        r.origin.x = 20
        r.origin.y = 20
    
        # Set the shadow.
        Quartz.CGContextSetShadow(context, scaleShadowOffset(offset), 3)
    
        # On Tiger and later, there is no need to use
        # a transparency layer to draw a PDF document as
        # a grouped object. On Panther, you can do so
        # by using a transparency layer. Drawing collected in
        # this transparency layer is drawn with the shadow
        # when the layer is ended.
        Quartz.CGContextBeginTransparencyLayer(context, None)
        if 1:
            Quartz.CGContextDrawPDFDocument(context, r, pdfDoc, 1)
    
        Quartz.CGContextEndTransparencyLayer(context)

.. rst-class:: tabbertab

UIHandling.py
.............

.. sourcecode:: python

    # ***    Drawing Basics ***
    
    kHICommandSimpleRect = 1000
    kHICommandStrokedRect = 1001
    kHICommandStrokedAndFilledRect = 1002
    kHICommandPathRects = 1003
    kHICommandAlphaRects = 1004
    kHICommandDashed = 1005
    kHICommandSimpleClip = 1006
    kHICommandPDFDoc = 1007
    
    # ***    Coordinate System ***
    kHICommandRotatedEllipses = 1008
    kHICommandDrawSkewCoordinates = 1082
    
    # ***    Path Drawing ***
    kHICommandBezierEgg = 1009
    kHICommandRoundedRects = 1010
    kHICommandStrokeWithCTM = 1011
    kHICommandRotatedEllipsesWithCGPath = 1012
    kHICommandPixelAligned = 1099
    
    # ***    Color and GState ***
    kHICommandDeviceFillAndStrokeColor = 1013
    kHICommandCLUTDrawGraphics = 1014
    kHICommandDrawWithGlobalAlpha = 1015
    kHICommandDrawWithBlendMode = 1068
    kHICommandDrawWithColorRefs = 1016
    kHICommandFunctionsHaveOwnGSave = 1017
    
    # ***    Images ***
    kHICommandDrawJPEGImage = 1018
    kHICommandColorImageFromFile = 1019
    kHICommandColorImageFromData = 1020
    kHICommandColorImageFromCallbacks = 1021
    kHICommandGrayRamp = 1022
    kHICommandDrawWithCGImageSource = 1023
    kHICommandDrawWithCGImageSourceIncremental = 1024
    kHICommandDrawWithQuickTime = 1025
    kHICommandSubstituteImageProfile = 1026
    kHICommandDoSubImage = 1027
    kHICommandExportWithQuickTime = 1028
    
    # ***    Image Masking ***
    kHICommandMaskTurkeyImage = 1029
    kHICommandImageMaskedWithMask = 1030
    kHICommandImageMaskedWithGrayImage = 1031
    kHICommandMaskImageWithColor = 1033
    kHICommandClipToMask = 1080
    kHICommandExportWithCGImageDestination = 1034
    
    # *** Bitmap Graphics Context and CGLayerRef ***
    kHICommandSimpleCGLayer = 1090
    kHICommandAlphaOnlyContext = 1091
    kHICommandDrawNoOffScreenImage = 1035
    kHICommandDrawOffScreenImage = 1036
    kHICommandDrawWithLayer = 1037
    
    # ***    Text ***
    kHICommandQuartzRomanText = 1038
    kHICommandQuartzTextModes = 1039
    kHICommandQuartzTextMatrix = 1040
    
    kHICommandDrawNSString = 1041
    kHICommandDrawNSLayoutMgr = 1042
    kHICommandDrawCustomNSLayoutMgr = 1043
    
    # ***    Advanced Drawing ***
    kHICommandSimplePattern = 1050
    kHICommandPatternMatrix = 1051
    kHICommandPatternPhase = 1052
    kHICommandUncoloredPattern = 1053
    kHICommandDrawWithPDFPattern = 1054
    
    kHICommandSimpleShadow = 1055
    kHICommandShadowScaling = 1056
    kHICommandShadowProblems = 1057
    kHICommandComplexShadow = 1058
    
    kHICommandMultipleShapeComposite = 1059
    kHICommandFillAndStrokeWithShadow = 1085
    kHICommandPDFDocumentShadow = 1060
    
    kHICommandSimpleAxialShading = 1061
    kHICommandExampleAxialShadings = 1062
    kHICommandSimpleRadialShading = 1063
    kHICommandExampleRadialShadings = 1064
    kHICommandEllipseShading = 1065
    
    # *** EPS drawing ***
    kHICommandDoCompatibleEPS = 1066

.. rst-class:: tabbertab

Utilities.py
............

.. sourcecode:: python

    import math
    
    import Cocoa
    import Quartz
    
    
    class ExportInfo:
        command = None
        fileType = None
        useQTForExport = False
        dpi = 1
    
    
    def DEGREES_TO_RADIANS(degrees):
        return degrees * math.pi / 180
    
    
    gScalingFactor = 1.0
    
    
    # These routines are used for getting the correct results when
    # drawing shadows and patterns with Quartz and exporting the
    # results as bits. This is a hack; in principle the scaling
    # factor should be passed to the draw proc.
    def setScalingFactor(scalingFactor):
        global gScalingFactor
        if scalingFactor > 0:
            gScalingFactor = scalingFactor
    
    
    def getScalingFactor():
        return gScalingFactor
    
    
    _appBundle = None
    
    
    def getAppBundle():
        global _appBundle
    
        if _appBundle is None:
            _appBundle = Cocoa.CFBundleGetMainBundle()
    
        return _appBundle
    
    
    #
    #    This version of getTheRGBColorSpace returns
    #    the DeviceRGB color space.
    #
    _deviceRGB = None
    
    
    def getTheRGBColorSpace():
        global _deviceRGB
    
        # Set once, the first time this function is called.
        if _deviceRGB is None:
            _deviceRGB = Quartz.CGColorSpaceCreateDeviceRGB()
    
        return _deviceRGB
    
    
    _genericRGBColorSpace = None
    
    
    def getTheCalibratedRGBColorSpace():
        global _genericRGBColorSpace
    
        if _genericRGBColorSpace is None:
            _genericRGBColorSpace = Quartz.CGColorSpaceCreateWithName(
                Quartz.kCGColorSpaceGenericRGB
            )
    
        return _genericRGBColorSpace
    
    
    _genericGrayColorSpace = None
    
    
    def getTheCalibratedGrayColorSpace():
        global _genericGrayColorSpace
    
        if _genericGrayColorSpace is None:
            _genericGrayColorSpace = Quartz.CGColorSpaceCreateWithName(
                Quartz.kCGColorSpaceGenericGray
            )
        return _genericGrayColorSpace
    
    
    _genericSRGBColorSpace = None
    
    
    def getTheSRGBColorSpace():
        # This only works on 10.5 or later
        global _genericSRGBColorSpace
    
        if _genericSRGBColorSpace is None:
            _genericSRGBColorSpace = Quartz.CGColorSpaceCreateWithName(
                Quartz.kCGColorSpaceGenericRGB
            )  # XXX: should be GenericSRGB
        return _genericSRGBColorSpace
    
    
    def getTheDisplayColorSpace():
        # This is a hack, basically here because the C implementation uses APIs that
        # aren't wrapped yet.
        return getTheRGBColorSpace()
    
    
    _rgbWhite = None
    
    
    def getRGBOpaqueWhiteColor():
        global _rgbWhite
    
        if _rgbWhite is None:
            opaqueWhite = (1.0, 1.0, 1.0, 1.0)
            _rgbWhite = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueWhite)
        return _rgbWhite
    
    
    _rgbBlack = None
    
    
    def getRGBOpaqueBlackColor():
        global _rgbBlack
        if _rgbBlack is None:
            opaqueBlack = (0, 0, 0, 1)
            _rgbBlack = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueBlack)
        return _rgbBlack
    
    
    _rgbGray = None
    
    
    def getRGBOpaqueGrayColor():
        global _rgbGray
        if _rgbGray is None:
            opaqueGray = (0.9, 0.9, 0.9, 1)
            _rgbGray = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueGray)
        return _rgbGray
    
    
    _rgbRed = None
    
    
    def getRGBOpaqueRedColor():
        global _rgbRed
        if _rgbRed is None:
            opaqueRed = (0.663, 0, 0.031, 1)
            _rgbRed = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueRed)
        return _rgbRed
    
    
    _rgbBlue = None
    
    
    def getRGBOpaqueBlueColor():
        global _rgbBlue
        if _rgbBlue is None:
            opaqueBlue = (0.482, 0.62, 0.871, 1)
            _rgbBlue = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueBlue)
        return _rgbBlue
    
    
    _rgbPurple = None
    
    
    def getRGBOpaquePurpleColor():
        global _rgbPurple
        if _rgbPurple is None:
            opaquePurple = (0.69, 0.486, 0.722, 1)
            _rgbPurple = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaquePurple)
        return _rgbPurple
    
    
    _rgbDarkBlue = None
    
    
    def getRGBOpaqueDarkBlueColor():
        global _rgbDarkBlue
        if _rgbDarkBlue is None:
            opaqueDarkBlue = (0.11, 0.208, 0.451, 1)
            _rgbDarkBlue = Quartz.CGColorCreate(
                getTheCalibratedRGBColorSpace(), opaqueDarkBlue
            )
        return _rgbDarkBlue
    
    
    _rgbBrown = None
    
    
    def getRGBOpaqueBrownColor():
        global _rgbBrown
        if _rgbBrown is None:
            opaqueBrown = (0.325, 0.208, 0.157, 1)
            _rgbBrown = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueBrown)
        return _rgbBrown
    
    
    _rgbOrange = None
    
    
    def getRGBOpaqueOrangeColor():
        global _rgbOrange
        if _rgbOrange is None:
            opaqueOrange = (0.965, 0.584, 0.059, 1)
            _rgbOrange = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueOrange)
        return _rgbOrange
    
    
    _rgbYellow = None
    
    
    def getRGBOpaqueYellowColor():
        global _rgbYellow
        if _rgbYellow is None:
            opaqueYellow = (1, 0.816, 0, 1)
            _rgbYellow = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueYellow)
        return _rgbYellow
    
    
    _rgbGreen = None
    
    
    def getRGBOpaqueGreenColor():
        global _rgbGreen
        if _rgbGreen is None:
            opaqueGreen = (0.584, 0.871, 0.318, 1)
            _rgbGreen = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueGreen)
        return _rgbGreen
    
    
    _rgbDarkGreen = None
    
    
    def getRGBOpaqueDarkGreenColor():
        global _rgbDarkGreen
        if _rgbDarkGreen is None:
            opaqueDarkGreen = (0.404, 0.808, 0.239, 1)
            _rgbDarkGreen = Quartz.CGColorCreate(
                getTheCalibratedRGBColorSpace(), opaqueDarkGreen
            )
        return _rgbDarkGreen
    
    
    def myCGContextAddEllipseInRect(context, r):
        if hasattr(Quartz, "CGContextAddEllipseInRect"):
            Quartz.CGContextAddEllipseInRect(context, r)
    
        else:
            # This is not a perfect emulation but is correct as long as there is
            # not an open subpath already in the current path. In that case the
            # CGContextClosePath here would not necessarily produce the desired
            # result.
            Quartz.CGContextSaveGState(context)
            if 1:
                # Translate to the center of the ellipse.
                Quartz.CGContextTranslateCTM(
                    context, Quartz.CGRectGetMidX(r), Quartz.CGRectGetMidY(r)
                )
                # Scale by half the width and height of the rectangle
                # bounding the ellipse.
                Quartz.CGContextScaleCTM(context, r.size.width / 2, r.size.height / 2)
                # Establish a current point at the first point
                # on the ellipse. This ensures that there
                # is no line segment connecting the previous
                # current point to the first point on the subpath.
                Quartz.CGContextMoveToPoint(context, 1, 0)
                # Circular arc around the ellipse center with
                # a radius that, when scaled by the CTM, produces
                # the major and minor axes of the ellipse. Since
                # CGContextAddEllipseInRect defines the direction
                # of the path as clockwise, this routine will
                # draw the arc clockwise also.
                Quartz.CGContextAddArc(context, 0, 0, 1, 0, 2 * math.pi, 1)
                Quartz.CGContextClosePath(context)
            Quartz.CGContextRestoreGState(context)
    
    
    # Routines that are useful for debugging.
    
    
    def drawPoint(context, p):
        Quartz.CGContextSaveGState(context)
        if 1:
            # Opaque black.
            Quartz.CGContextSetRGBStrokeColor(context, 0, 0, 0, 1)
            Quartz.CGContextSetLineWidth(context, 5)
            Quartz.CGContextSetLineCap(context, Quartz.kCGLineCapRound)
            Quartz.CGContextMoveToPoint(context, p.x, p.y)
            Quartz.CGContextAddLineToPoint(context, p.x, p.y)
            Quartz.CGContextStrokePath(context)
        Quartz.CGContextRestoreGState(context)
    
    
    def printCTM(context):
        t = Quartz.CGContextGetCTM(context)
        print(f"CurrentCTM is {t!r}")
    
    
    kTickLength = 5
    kTickDistance = 72
    kAxesLength = 20 * kTickDistance
    
    
    def drawCoordinateAxes(context):
        tickLength = kTickLength
    
        Quartz.CGContextSaveGState(context)
        if 1:
            Quartz.CGContextBeginPath(context)
            # Paint the x-axis in red.
            Quartz.CGContextSetRGBStrokeColor(context, 1, 0, 0, 1)
            Quartz.CGContextMoveToPoint(context, -kTickLength, 0.0)
            Quartz.CGContextAddLineToPoint(context, kAxesLength, 0.0)
            Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke)
    
            # Paint the y-axis in blue.
            Quartz.CGContextSetRGBStrokeColor(context, 0, 0, 1, 1)
            Quartz.CGContextMoveToPoint(context, 0, -kTickLength)
            Quartz.CGContextAddLineToPoint(context, 0, kAxesLength)
            Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke)
    
            # Paint the x-axis tick marks in red.
            Quartz.CGContextSetRGBStrokeColor(context, 1, 0, 0, 1)
            for _ in range(2):
                for t in range(0, kAxesLength, kTickDistance):
                    Quartz.CGContextMoveToPoint(context, t, -tickLength)
                    Quartz.CGContextAddLineToPoint(context, t, tickLength)
    
                Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke)
                Quartz.CGContextRotateCTM(context, math.pi / 2.0)
                # Paint the y-axis tick marks in blue.
                Quartz.CGContextSetRGBStrokeColor(context, 0, 0, 1, 1)
    
            drawPoint(context, Quartz.CGPointZero)
        Quartz.CGContextRestoreGState(context)
    
    
    def drawDebuggingRect(context, rect):
        Quartz.CGContextSaveGState(context)
        if 1:
            Quartz.CGContextSetLineWidth(context, 4.0)
            # Draw opaque red from top-left to bottom-right.
            Quartz.CGContextSetRGBStrokeColor(context, 1, 0, 0, 1.0)
            Quartz.CGContextMoveToPoint(
                context, rect.origin.x, rect.origin.y + rect.size.height
            )
            Quartz.CGContextAddLineToPoint(
                context, rect.origin.x + rect.size.width, rect.origin.y
            )
            Quartz.CGContextStrokePath(context)
            # Draw opaque blue from top-right to bottom-left.
            Quartz.CGContextSetRGBStrokeColor(context, 0, 0, 1, 1.0)
            Quartz.CGContextMoveToPoint(
                context, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height
            )
            Quartz.CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y)
            Quartz.CGContextStrokePath(context)
            # Opaque black.
            Quartz.CGContextSetRGBStrokeColor(context, 0, 0, 0, 1.0)
            Quartz.CGContextStrokeRect(context, rect)
        Quartz.CGContextRestoreGState(context)

.. rst-class:: tabbertab

main.py
.......

.. sourcecode:: python

    # Make sure all code is loaded
    import AppDrawing  # noqa: F401
    import BitmapContext  # noqa: F401
    import ColorAndGState  # noqa: F401
    import CoordinateSystem  # noqa: F401
    import DataProvidersAndConsumers  # noqa: F401
    import DrawingBasics  # noqa: F401
    import EPSPrinting  # noqa: F401
    import FrameworkTextDrawing  # noqa: F401
    import FrameworkUtilities  # noqa: F401
    import ImageMasking  # noqa: F401
    import Images  # noqa: F401
    import MyAppController  # noqa: F401
    import MyView  # noqa: F401
    import PathDrawing  # noqa: F401
    import PatternDrawing  # noqa: F401
    import PDFHandling  # noqa: F401
    import QuartzTextDrawing  # noqa: F401
    import Shadings  # noqa: F401
    import ShadowsAndTransparencyLayers  # noqa: F401
    import Utilities  # noqa: F401
    from PyObjCTools import AppHelper
    
    AppHelper.runEventLoop()

.. rst-class:: tabbertab

setup.py
........

.. sourcecode:: python

    """
    Script for building the example.
    
    Usage:
        python3 setup.py py2app
    """
    
    import os
    
    from setuptools import setup
    
    setup(
        name="BasicDrawing",
        app=["main.py"],
        data_files=["English.lproj"]
        + [os.path.join("GraphicsFiles", fn) for fn in os.listdir("GraphicsFiles")],
        setup_requires=["py2app", "pyobjc-framework-Cocoa", "pyobjc-framework-Quartz"],
    )

